2008-11-23 16 views
9

मैं 1.4 मिलियन लाइनों के साथ एक बड़ी टेक्स्ट फ़ाइल में पढ़ रहा हूं जो आकार में 24 एमबी (औसत 17 वर्ण एक पंक्ति) है।डेल्फी में स्ट्रिंग्स के लिए अतिरिक्त मेमोरी क्यों?

मैं डेल्फी 200 का उपयोग कर रहा हूं और फ़ाइल एएनएसआई है लेकिन पढ़ने पर यूनिकोड में परिवर्तित हो जाती है, इसलिए आप कह सकते हैं कि एक बार कनवर्ट किया गया टेक्स्ट 48 एमबी आकार में है।

(संपादित करें: मैं एक बहुत सरल उदाहरण ... पाया जाता है):

AllLines := TStringList.Create; 
    AllLines.LoadFromFile(Filename); 

मैं डेटा की तर्ज कि पाया

मैं एक साधारण StringList में इस पाठ लोड हो रहा हूँ लगता है कि उनके 48 एमबी की तुलना में अधिक स्मृति लेना प्रतीत होता है।

वास्तव में, वे 155 एमबी मेमोरी का उपयोग करते हैं।

मुझे 48 एमबी का उपयोग करके डेल्फी या कुछ मेमोरी प्रबंधन ओवरहेड के लिए 60 एमबी की अनुमति नहीं है। लेकिन 155 एमबी अत्यधिक लगता है।

यह स्ट्रिंगलिस्ट की गलती नहीं है। मैंने पहले लाइनों को रिकॉर्ड संरचना में लोड करने का प्रयास किया था, और मुझे एक ही परिणाम मिला (160 एमबी)।

मैं नहीं देखता या समझता हूं कि डेल्फी या फास्टएमएम मेमोरी मैनेजर स्ट्रिंग को स्टोर करने के लिए आवश्यक स्मृति की 3 गुणा का उपयोग करने के कारण क्या हो सकता है। ढेर आवंटन अक्षम नहीं हो सकता है, है ना?

मैंने इसे डीबग किया है और जहां तक ​​मैं कर सकता हूं इसका शोध किया है। यह क्यों हो सकता है कि यह क्यों हो रहा है, या विचार जो मुझे अतिरिक्त उपयोग को कम करने में मदद कर सकते हैं, की सराहना की जाएगी।

नोट: मैं इस उदाहरण के रूप में इस "छोटी" फ़ाइल का उपयोग कर रहा हूं। मैं वास्तव में एक 320 एमबी फ़ाइल लोड करने की कोशिश कर रहा हूं, लेकिन डेल्फी 2 जीबी रैम से अधिक पूछ रहा है और इस अतिरिक्त स्ट्रिंग आवश्यकता के कारण स्मृति से बाहर निकल रहा है।

एडिनम: मार्को कैंटू बस a White Paper on Delphi and Unicode के साथ बाहर आया। डेल्फी 200 ने प्रति स्ट्रिंग ओवरहेड 8 बाइट से 12 बाइट्स तक बढ़ाया है (प्लस स्ट्रिंग के लिए वास्तविक पॉइंटर के लिए शायद 4 और अधिक)। 17x2 = 34 बाइट लाइन प्रति अतिरिक्त 16 बाइट लगभग 50% जोड़ता है। लेकिन मैं 200% से अधिक ओवरहेड देख रहा हूं। अतिरिक्त 150% क्या हो सकता है?


सफलता !! आपके सुझावों के लिए आप सभी को धन्यवाद। तुम सब मुझे सोचते हो। लेकिन मुझे उत्तर के लिए जन गोइवार्ट्स क्रेडिट देना होगा, क्योंकि उन्होंने पूछा:

... आप TStringList का उपयोग क्यों कर रहे हैं? फ़ाइल को वास्तव में स्मृति में अलग लाइनों के रूप में संग्रहीत किया जाना चाहिए?

इससे मुझे समाधान हुआ कि 24 एमबी फ़ाइल को 1.4 मिलियन लाइन स्ट्रिंगलिस्ट के रूप में लोड करने की बजाय, मैं अपनी लाइनों को प्राकृतिक समूहों में समूहित कर सकता हूं, जो मेरे कार्यक्रम के बारे में जानते हैं। तो इसके परिणामस्वरूप स्ट्रिंग सूची में 127,000 लाइनें लोड हुईं।

अब प्रत्येक पंक्ति 17 के बजाय 190 वर्णों का औसत है। स्ट्रिंगलिस्ट लाइन पर ओवरहेड समान है लेकिन अब बहुत कम लाइनें हैं।

जब मैं इसे 320 एमबी फ़ाइल पर लागू करता हूं, तो यह अब स्मृति से बाहर नहीं चला जाता है और अब 1 जीबी से कम रैम में लोड होता है। (और लोड करने में केवल 10 सेकंड लगते हैं, जो बहुत अच्छा है!)

समूहबद्ध रेखाओं को पार्स करने के लिए थोड़ा अतिरिक्त प्रसंस्करण होगा, लेकिन यह प्रत्येक समूह के वास्तविक समय प्रसंस्करण में ध्यान देने योग्य नहीं होना चाहिए।

(यदि आप सोच रहे थे, तो यह एक वंशावली कार्यक्रम है, और यह अंतिम चरण हो सकता है जो मुझे 30 सेकंड से कम समय में 32-बिट एड्रेस स्पेस में लगभग दस लाख लोगों को लोड करने की अनुमति देने के लिए आवश्यक हो सकता है इसलिए मुझे अभी भी डेटा में डिस्प्ले और संपादन की अनुमति देने के लिए आवश्यक डेटा में इंडेक्स जोड़ने के लिए 20 सेकंड बफर मिला है।)

+0

आप जो स्मृति लेते हैं उसे मापते हैं? मुझे उम्मीद है कि कार्य प्रबंधक से मेम उपयोग कॉलम के साथ नहीं। यह माप नहीं रहा है कि आप क्या सोच सकते हैं। –

+0

स्मृति माप के लिए, मैं GlobalMemoryStatusEx का उपयोग करता हूं। देखें: http://msdn.microsoft.com/en-us/library/aa366589(VS.85).aspx – lkessler

+0

आपको यह जांचना चाहिए कि वास्तव में डेल्फी में कितनी मेमोरी का उपयोग किया जाता है। डेल्फी एमएम ओएस से प्राप्त होने वाले बड़े ब्लॉक को आवंटित करेगा, और जब संभव हो तो उन्हें केवल ओएस में छोड़ दें (विखंडन और जैसे इसे अस्वीकार कर सकते हैं), तो विंडोज क्या देखता है और डेल्फी क्या अलग हो सकता है। यदि आप Sourceforge से उपलब्ध पूर्ण फास्टएमएम लाइब्रेरी का उपयोग करते हैं तो इसमें एमएम आवंटन से पूछताछ करने की सुविधा है जो आपको क्या हो रहा है पर गहराई से देखता है। अन्यथा आप इसे जांचने के लिए एक मेमोरी प्रोफाइलर (यानी AQTime) का उपयोग कर सकते हैं और देख सकते हैं कि आवंटित स्मृति, कब और क्यों। –

उत्तर

9

आपने मुझसे व्यक्तिगत रूप से यहां अपने प्रश्न का उत्तर देने के लिए कहा है। मुझे सटीक कारण नहीं पता कि आप इस तरह के उच्च स्मृति उपयोग क्यों देख रहे हैं, लेकिन आपको यह याद रखना होगा कि TStringList बस अपनी फ़ाइल लोड करने से बहुत कुछ करता है। इन चरणों में से प्रत्येक को स्मृति की आवश्यकता होती है जिसके परिणामस्वरूप स्मृति विखंडन हो सकता है। TStringList को आपकी फ़ाइल को स्मृति में लोड करने की आवश्यकता है, इसे Ansi से यूनिकोड में परिवर्तित करें, इसे प्रत्येक पंक्ति के लिए एक स्ट्रिंग में विभाजित करें, और उन पंक्तियों को एक सरणी में भरें जिन्हें कई बार फिर से आवंटित किया जाएगा।

मेरा प्रश्न है कि आप TStringList का उपयोग क्यों कर रहे हैं? फ़ाइल को वास्तव में स्मृति में अलग लाइनों के रूप में संग्रहीत किया जाना चाहिए? क्या आप फ़ाइल को मेमोरी में संशोधित करने जा रहे हैं, या बस इसके कुछ हिस्सों को प्रदर्शित कर रहे हैं?फाइल को मेमोरी में एक बड़े हिस्से के रूप में रखते हुए और नियमित रूप से अभिव्यक्तियों के साथ पूरी चीज को स्कैन करना जो आपके इच्छित हिस्सों से मेल खाता है, अलग-अलग लाइनों को संग्रहित करने से अधिक मेमोरी कुशल होगा।

साथ ही, पूरी फ़ाइल को यूनिकोड में परिवर्तित किया जाना चाहिए? जबकि आपका एप्लिकेशन यूनिकोड है, आपकी फ़ाइल अंसी है। मेरी सामान्य सिफारिश है कि एन्सी इनपुट को जल्द से जल्द यूनिकोड में परिवर्तित करना है, क्योंकि ऐसा करने से CPU चक्र बचाता है। लेकिन जब आपके पास 320 एमबी का अंसी डेटा है जो Ansi डेटा के रूप में रहेगा, तो स्मृति खपत बाधा होगी। फ़ाइल को स्मृति में Ansi के रूप में रखने का प्रयास करें, और केवल उन हिस्सों को रूपांतरित करें जिन्हें आप उपयोगकर्ता को Ansi के रूप में प्रदर्शित करेंगे।

यदि 320 एमबी फ़ाइल डेटा फ़ाइल नहीं है तो आप कुछ जानकारी निकाल रहे हैं, लेकिन एक डेटा सेट जिसे आप संशोधित करना चाहते हैं, इसे एक रिलेशनल डेटाबेस में परिवर्तित करने पर विचार करें, और डेटाबेस इंजन को चिंता करने दें कि कैसे प्रबंधित करें सीमित रैम के साथ डेटा का विशाल सेट।

+0

आपके विचारों के लिए जनवरी धन्यवाद, जो मुझे सोचने के लिए और अधिक देता है। "खंड" का आपका सुझाव मुझे तारों के समूहों को लोड करने का प्रयास करना चाहता है, जो प्रति पंक्ति 17 वर्णों की बजाय प्रति समूह के लगभग 150 वर्ण औसत है। वंशावली सॉफ्टवेयर यूनिकोड होना चाहिए। – lkessler

+1

बेशक आपका सॉफ्टवेयर यूनिकोड होना चाहिए। लेकिन इसका मतलब यह नहीं है कि यूनिकोड में जब आप स्रोत यूनिकोड नहीं हैं, तो आपको यूनिकोड में 320 एमबी डेटा मेमोरी में रखने की आवश्यकता है। –

1

क्या आप विंडोज़ पर निर्भर हैं कि आपको कितनी मेमोरी है कार्यक्रम का उपयोग कर रहा है? डेल्फी ऐप द्वारा उपयोग की जाने वाली मेमोरी को ओवरस्टेट करने के लिए यह कुख्यात है।

मुझे आपके कोड में बहुत अधिक मेमोरी उपयोग दिखाई देता है, हालांकि।

आपकी रिकॉर्ड संरचना 20 बाइट्स है - यदि प्रति पंक्ति एक ऐसा रिकॉर्ड है तो आप पाठ के मुकाबले रिकॉर्ड के लिए अधिक डेटा देख रहे हैं।

इसके अलावा, एक स्ट्रिंग में एक अंतर्निहित 4 बाइट ओवरहेड होता है - एक और 25%।

मेरा मानना ​​है कि डेल्फी के ढेर हैंडलिंग में आवंटन ग्रैन्युलरिटी की एक निश्चित राशि है लेकिन मुझे याद नहीं है कि वर्तमान में यह क्या है। यहां तक ​​कि 8 बाइट्स (फ्री ब्लॉक की एक लिंक्ड सूची के लिए दो पॉइंटर्स) पर आप 25% देख रहे हैं।

ध्यान दें कि हम पहले से ही 150% से अधिक वृद्धि कर रहे हैं।

+0

यूनिकोडस्ट्रिंग का ओवरहेड लम्बाई के लिए चार बाइट्स, संदर्भ गणना के लिए चार बाइट्स, और अंत में शून्य के लिए दो बाइट्स है। –

+0

रिकॉर्ड के साथ मेरे पिछले उदाहरण में, मैंने विशेष रूप से कहा था कि मैं रिकॉर्ड लोड करने और स्ट्रिंग को निर्दिष्ट किए बिना रिकॉर्ड लोड करने के लिए स्ट्रिंग को असाइन करने की तुलना कर रहा था। इसलिए अंतर अकेले स्ट्रिंग के कारण था, और रिकॉर्ड में 20 बाइट नहीं। – lkessler

8

क्या होगा यदि आपने अपना मूल रिकॉर्ड AnsiString का उपयोग किया है? यह तुरंत आधे में चॉप करता है? सिर्फ इसलिए कि डेल्फी यूनिकोडस्ट्रिंग के लिए डिफ़ॉल्ट है इसका मतलब यह नहीं है कि आपको इसका उपयोग करना है।

इसके अतिरिक्त, यदि आप प्रत्येक स्ट्रिंग (एक वर्ण या दो के भीतर) की लंबाई को जानते हैं तो छोटे स्ट्रिंग का उपयोग करना और कुछ और बाइट्स को दाढ़ी देना बेहतर हो सकता है।

मैं उत्सुक हूं कि आप जो करने की कोशिश कर रहे हैं उसे पूरा करने का एक बेहतर तरीका हो सकता है। मेमोरी में 320 एमबी पाठ को लोड करना शायद सबसे अच्छा समाधान नहीं हो सकता है, भले ही आप इसे केवल 320 एमबी

+0

अच्छा जवाब और मैं इसके बारे में सोचूंगा। मेरा प्रोग्राम यूनिकोड के लिए डिज़ाइन किया गया है, इसलिए बहुत बड़ी फ़ाइलों के लिए एएनएसआई को वापस लेना शर्म की बात होगी। मैं फ़ाइल मेमोरी मैपिंग का प्रयास कर सकता हूं। मुझे उम्मीद नहीं है कि मुझे जो चाहिए वह पर्याप्त तेज़ होगा - लेकिन जब तक आप कोशिश नहीं करते तब तक आप कभी नहीं जानते। – lkessler

4

डिफ़ॉल्ट रूप से, डेल्फी 200 की टीस्ट्रिंगलिस्ट एक फ़ाइल को एएनएसआई के रूप में पढ़ता है, जब तक बाइट ऑर्डर मार्क न हो फ़ाइल को किसी और चीज़ के रूप में पहचानें, या यदि आप लोडफ्रॉमफ़ाइल के वैकल्पिक दूसरे पैरामीटर के रूप में एन्कोडिंग प्रदान करते हैं।

तो यदि आप देख रहे हैं कि TStringList आपके विचार से अधिक स्मृति ले रहा है, तो कुछ और चल रहा है।

+0

धन्यवाद, निक। हमम ... कल्पना नहीं कर सकते कि और क्या चल रहा है। मेरा उदाहरण काफी सरल है। – lkessler

3

क्या आप स्रोत फोर्ज से फास्टएमएम स्रोतों के साथ प्रोग्राम को संकलित करने और फुलडेबगोड परिभाषित करने के किसी भी मौके से हैं? उस स्थिति में, फास्टएमएम वास्तव में अप्रयुक्त स्मृति ब्लॉक जारी नहीं कर रहा है, जो समस्या की व्याख्या करेगा।

+0

अच्छा विचार, लेकिन नहीं। मैं डेल्फी 200 9 में फास्टएमएम का उपयोग कर रहा हूं। एकमात्र विकल्प जिसे मैंने बदल दिया है, स्ट्रिंग प्रारूप जांच बंद करने के लिए संकलक विकल्प है, जैसा कि कई ब्लॉगों पर अनुशंसित किया गया है। – lkessler

6

मैं डेल्फी 200 का उपयोग कर रहा हूं और फ़ाइल एएनएसआई है लेकिन पढ़ने पर यूनिकोड में परिवर्तित हो जाती है, इसलिए आप कह सकते हैं कि एक बार कनवर्ट किया गया टेक्स्ट 48 एमबी आकार में है।

क्षमा करें, लेकिन मुझे यह बिल्कुल समझ में नहीं आता है। यदि आपको अपने प्रोग्राम को यूनिकोड होने की आवश्यकता है, निश्चित रूप से फ़ाइल "एएनएसआई" है (इसमें कुछ वर्ण सेट होना चाहिए, जैसे WIN1252 या ISO8859_1) सही बात नहीं है। मैं इसे पहले यूटीएफ 8 के रूप में परिवर्तित कर दूंगा। अगर फ़ाइल में कोई वर्ण नहीं है> = 128 यह किसी चीज़ को नहीं बदलेगा (यह भी वही आकार होगा), लेकिन आप भविष्य के लिए तैयार हैं।

अब आप इसे यूटीएफ 8 तारों में लोड कर सकते हैं, जो आपकी स्मृति खपत को दोगुना नहीं करेगा। डेल्फी यूनिकोड स्ट्रिंग के साथ स्क्रीन पर दिखाई देने वाले कुछ तारों पर ऑन-द-फ्लाई-रूपांतरण धीमा हो जाएगा, लेकिन छोटी मेमोरी पदचिह्न को देखते हुए आपका प्रोग्राम सिस्टम (सिस्टम) पर बहुत बेहतर प्रदर्शन करेगा। याद।

अब अपने कार्यक्रम अभी भी बहुत अधिक स्मृति TStringList के साथ आप हमेशा आप इस कार्यक्रम में TStrings या यहाँ तक कि IStrings उपयोग कर सकते हैं, और एक वर्ग है कि IStrings लागू करता है या TStrings विरासत और स्मृति में सभी लाइनों नहीं रखता लिखने की खपत करता है, तो। कुछ विचार जो मन में आते हैं:

  1. एक TMemoryStream में फ़ाइल पढ़ें, और लाइनों के पहले अक्षर के लिए संकेत की एक सरणी बनाए रखें। एक स्ट्रिंग को वापस करना आसान है, तो आपको सीआर और एनएल छीनने के साथ ही लाइन की शुरुआत और अगले की शुरुआत के बीच एक उचित स्ट्रिंग वापस करने की आवश्यकता है।

  2. यदि यह अभी भी बहुत अधिक स्मृति का उपभोग करता है, तो टीएमाइलस्ट्रीम के साथ टीएममेलरीस्ट्रीम को प्रतिस्थापित करें, और चार पॉइंटर्स की सरणी बनाए रखें, लेकिन लाइन के लिए फ़ाइल ऑफ़सेट की एक सरणी शुरू होती है।

  3. आप मेमोरी मैप की गई फ़ाइलों के लिए विंडोज एपीआई फ़ंक्शंस का भी उपयोग कर सकते हैं। यह आपको फ़ाइल ऑफ़सेट के बजाय मेमोरी पतों के साथ काम करने की अनुमति देता है, लेकिन पहले विचार के रूप में उस स्मृति को अधिक उपभोग नहीं करता है।

+0

आपके 3 विचार अच्छे हैं। लेकिन यूटीएफ 8 में कनवर्ट करना डेल्फी 200 9 में अक्षम और गलत है। मुझे या तो इसे एएनएसआई में रखना होगा और जब मुझे 24 एमबी अतिरिक्त (जिसे मैं करना चाहता हूं) को अवशोषित कर दूंगा और प्रोग्राम के लिए यूनिकोड में परिवर्तित करूँगा उपयोग करने के लिए। – lkessler

+0

क्षमा करें, लेकिन मुझे असहमत होना पड़ता है। यूटीएफ 8 डाटा स्टोरेज और डेटा एक्सचेंज के लिए सही प्रारूप है, और चूंकि आई/ओ सीपीयू प्रोसेसिंग की तुलना में बहुत धीमी है, इसलिए आपको न केवल छोटी डिस्क फाइलें, बल्कि बेहतर प्रदर्शन भी देना चाहिए। जो भी आंतरिक स्ट्रिंग प्रारूप है, मैं हमेशा डेटा फ़ाइलों के लिए यूटीएफ 8 का उपयोग करता हूं। – mghie

+1

डेटा फ़ाइलों अक्सर प्रोग्राम कोड से अधिक मूल्यवान होते हैं, इसलिए किसी विशेष प्रोग्रामिंग वातावरण के लिए अनुकूलन गलत है। उनके प्रारूप को अभिव्यक्तिपूर्ण लेकिन कुशल, अधिमानतः मानकीकृत होना चाहिए। यूटीएफ 8 आपको वह सब देता है, और विंडोज के बाहर भी सबसे आम है। क्या पसंद नहीं करना? – mghie

0

आप उस डेटा को TStringList में क्यों लोड कर रहे हैं? सूची में कुछ ओवरहेड होगा। शायद TTextReader आपकी मदद कर सकता है।

+0

TTextReader केवल इनपुट को पार्स करने में मदद करता है। मैं पहले से ही खुद को बहुत कुशलतापूर्वक करता हूं। मुझे फिर कुछ जगहों पर पार्स लाइनों को रखना होगा। मैंने मूल रूप से रिकॉर्ड का उपयोग करने की कोशिश की और इस स्मृति उपयोग समस्या को पाया। तब मुझे TStringList में एक ही समस्या मिली और प्रश्न पर एक सरल उदाहरण के रूप में छोड़ दिया। – lkessler

1

इसका हिस्सा ब्लॉक आवंटन एल्गोरिदम हो सकता है। जैसे-जैसे आपकी सूची बढ़ती है, यह प्रत्येक खंड में आवंटित स्मृति की मात्रा को बढ़ाना शुरू कर देता है। मैंने इसे लंबे समय तक नहीं देखा है, लेकिन मेरा मानना ​​है कि यह स्मृति के बाहर होने पर हर बार आवंटित की गई राशि को दोगुना करने जैसा कुछ करता है। जब आप बड़ी सूचियों से निपटना शुरू करते हैं, तो आपके आवंटन की अंततः आपके लिए बहुत बड़ी आवश्यकता होती है।

संपादित रूप lkessler ने कहा, इस वृद्धि वास्तव में केवल 25% है, लेकिन यह अभी भी समस्या का एक भाग के रूप में माना जाना चाहिए। यदि आप टिपिंग प्वाइंट से परे हैं, तो उस सूची में आवंटित स्मृति का एक विशाल ब्लॉक हो सकता है जिसका उपयोग नहीं किया जा रहा है।

+0

यह एक अच्छा सुझाव था, लेकिन TStringList.Grow केवल प्रत्येक बार 25% अधिक आकार बढ़ाता है। तो सबसे ऊपर की ओर यह 25% है। – lkessler