2011-08-25 27 views
5

में बहुत सारी मेमोरी लेने वाली वस्तुओं की TStringList मैं सिमुलेशन प्रोग्राम पर काम कर रहा हूं।डेल्फी XE

प्रोग्राम की पहली चीजों में से एक को एक बड़ी फ़ाइल (28 एमबी, लगभग 79'000 लाइनों) में पढ़ा जाता है, प्रत्येक पंक्ति (लगभग 150 फ़ील्ड) पार्स, ऑब्जेक्ट के लिए कक्षा बनाएं, और इसे जोड़ें एक TStringList।

यह एक और फ़ाइल में भी पढ़ता है, जो दौड़ के दौरान अधिक वस्तुओं को जोड़ता है। अंत में, यह लगभग 85'000 वस्तुओं के समाप्त होता है।

मैं डेल्फी 2007 के साथ काम कर रहा था, और कार्यक्रम ने बहुत मेमोरी का उपयोग किया, लेकिन यह ठीक चला गया। मैंने डेल्फी एक्सई में अपग्रेड किया, और प्रोग्राम को माइग्रेट कर दिया और अब यह बहुत अधिक मेमोरी का उपयोग कर रहा है, और यह रन के माध्यम से आधा रास्ते मेमोरी से बाहर हो जाता है।

तो डेल्फी 2007 में, यह प्रारंभिक फ़ाइल में पढ़ने के बाद 1.4 गीगा का उपयोग कर समाप्त हो जाएगा, जो स्पष्ट रूप से एक बड़ी राशि है, लेकिन एक्सई में, यह लगभग 1.8 गीगा का उपयोग करके समाप्त होता है, जो वास्तव में बड़ा होता है और दौड़ने की ओर जाता है बाहर और त्रुटि हो रही

तो मेरे सवाल का

  1. यह इतना स्मृति क्यों उपयोग कर रहा है?
  2. 2007 से XE में इतनी अधिक स्मृति का उपयोग क्यों कर रहा है?
  3. मैं इसके बारे में क्या कर सकता हूं? मैं बदल सकते हैं नहीं कितना बड़ा या लंबे फ़ाइल है, और यह कहीं

धन्यवाद

+1

क्या आप वाकई 1.4 और 2.8 नहीं हैं? –

+0

यूनिकोडिंग AnsiString से बड़ा है। यह टीस्ट्रिंगलिस्ट वर्ग के आधार पर है। – RBA

+1

जब तक कि आप 64 बिट प्रक्रियाओं का पूरी तरह से उपयोग करने पर भरोसा नहीं कर सकते हैं, आपको स्मृति को अधिक फ्रगल रूप से उपयोग करने के लिए अपने ऐप को फिर से डिजाइन करना चाहिए। 1.4 जीबी पर भी आप 32 बिट सिस्टम पर एड्रेस स्पेस की सीमाओं को दबाएंगे। –

उत्तर

7

के बीच

type 
    TAnsiStringArray = array of AnsiString; 
    // or 
    TUnicodeStringArray = array of string; // In Delphi 2009+, 
             // string = UnicodeString 
यह क्यों अपनी 28 एमबी फ़ाइल वस्तुओं की 1.4 जीबी लायक करने के लिए विस्तार हो रहा है कहने के लिए जब आप इसे वस्तुओं में पार्स आउट कर मुश्किल है कोड और वर्ग देखे बिना घोषणाओं। साथ ही, आप कहते हैं कि आप इसे में TList या TObjecList के बजाय संग्रहीत कर रहे हैं। ऐसा लगता है कि आप इसे किसी प्रकार की स्ट्रिंग-> ऑब्जेक्ट कुंजी/वैल्यू मैपिंग के रूप में उपयोग कर रहे हैं। यदि ऐसा है, तो आप XE में Generics.Collections इकाई में TDictionary कक्षा को देखना चाहेंगे।

क्यों आप XE में अधिक मेमोरी का उपयोग कर रहे हैं, ऐसा इसलिए है क्योंकि string प्रकार एएनएसआई स्ट्रिंग से डेल्फी 200 में यूटीएफ -16 स्ट्रिंग में बदल गया है। अगर आपको यूनिकोड की आवश्यकता नहीं है, तो आप एक TDictionary का उपयोग कर सकते हैं स्थान सुरक्षित करें।

इसके अलावा, और भी स्मृति को बचाने के लिए, यदि आप सभी 79,000 ऑब्जेक्ट्स की तुरंत आवश्यकता नहीं है, तो एक और चाल है जिसका उपयोग आप कर सकते हैं: आलसी लोडिंग।विचार इस तरह कुछ जाता है:

  • फ़ाइल को एक TStringList में पढ़ें। (यह फ़ाइल आकार के रूप में उतनी मेमोरी का उपयोग करेगा। शायद यह दो गुना अधिक होगा यदि यह यूनिकोड स्ट्रिंग में परिवर्तित हो जाता है।) कोई भी डेटा ऑब्जेक्ट न बनाएं।
  • जब आपको किसी विशिष्ट डेटा ऑब्जेक्ट की आवश्यकता होती है, तो उस स्ट्रिंग को कॉल करें जो स्ट्रिंग सूची की जांच करता है और उस ऑब्जेक्ट के लिए स्ट्रिंग कुंजी को देखता है।
  • जांचें कि क्या उस स्ट्रिंग के साथ एक ऑब्जेक्ट जुड़ा हुआ है। यदि नहीं, तो स्ट्रिंग से ऑब्जेक्ट बनाएं और इसे TStringList में स्ट्रिंग के साथ संबद्ध करें।
  • स्ट्रिंग से जुड़े ऑब्जेक्ट को वापस करें।

यह आपके मेमोरी उपयोग और आपके लोड समय दोनों को बनाए रखेगा, लेकिन यह केवल तभी उपयोगी होगा जब आपको लोड होने के तुरंत बाद ऑब्जेक्ट्स (या एक बड़ा प्रतिशत) की आवश्यकता न हो।

+0

उत्तर के लिए धन्यवाद। दुर्भाग्य से, मुझे सभी वस्तुओं की आवश्यकता है। कार्यक्रम एक अंग आवंटक है। मैंने रोगियों की प्रारंभिक प्रतीक्षा सूची में पढ़ा, और फिर मैं या तो एक नए रोगी में पढ़ता हूं, सूची में एक रोगी को अद्यतन करता हूं, या अंग आवंटित करने का प्रयास करता हूं, और जब मैं आवंटित करता हूं तो मुझे यह देखने के लिए सभी रोगियों को देखने की ज़रूरत होती है कि वे मेल खाते हैं (जिसके लिए सभी रोगी मूल्यों की आवश्यकता होती है) – KingOfKong

+13

@किंगऑफकॉन्ग: यदि आपके पास विशिष्ट मानदंड हैं तो आप इस तरह से मिलान करने का प्रयास कर रहे हैं, तो मैं आपके आर्किटेक्चर को बदलने का सुझाव दूंगा। आप जो वर्णन कर रहे हैं वह एक रिलेशनल डेटाबेस और SQL क्वेरी के लिए एकदम सही मिलान है। इस तरह से ऐसा करने से आपके मेमोरी उपयोग और खोज चलाने के लिए कितना समय लगता है नाटकीय रूप से कम हो जाएगा। –

+1

+1 जब तक आवश्यकता वास्तव में अल्ट्रा टाइम-क्रिटिकल नहीं है, तो खोज के दौरान फ्लाई पर एक ही ऑब्जेक्ट को पुन: असाइन करना ठीक काम करना चाहिए। यह मूल रूप से स्मृति उपयोग और अवधि के बीच एक विकल्प है। – NGLN

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

  • डेल्फी 200 (और बाद में) में, एक स्ट्रिंग एक यूनिकोड स्ट्रिंग है, यानी, प्रत्येक चरित्र में स्मृति के 2 बाइट हैं।

AFAIK, वहाँ एक डेल्फी 2009+ TStringList वस्तु उपयोग Ansi तार बनाने के लिए कोई रास्ता नहीं है। क्या आप वास्तव में TStringList की किसी भी सुविधा का उपयोग कर रहे हैं? यदि नहीं, तो आप इसके बजाय तारों की सरणी का उपयोग कर सकते हैं।

फिर, स्वाभाविक रूप से, आप चुन सकते

+0

केवल एक चीज जो मैं वास्तव में करना चाहता हूं वह वस्तुओं को संग्रहित करती है और उसके बाद उन्हें हटाने/जोड़ने का एक तरीका होता है। मैं – KingOfKong

+1

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

+0

समस्या ऑब्जेक्ट्स हो सकती है, उनके पास लगभग 60 फ़ील्ड हैं, जिनमें से लगभग 10 सूचियां हैं। अधिकांश सूचियां छोटी हैं लेकिन विशेष रूप से 200 फ़ील्ड लंबी – KingOfKong

2

एंड्रियास 'पोस्ट के अलावा:

डेल्फी 2009 से पहले, एक स्ट्रिंग हैडर 8 बाइट्स कब्जा कर लिया। डेल्फी 200 से शुरू, एक स्ट्रिंग हेडर 12 बाइट लेता है। इसलिए प्रत्येक अद्वितीय स्ट्रिंग पहले से 4 बाइट्स का उपयोग करती है, + तथ्य यह है कि प्रत्येक चरित्र स्मृति में दो बार लेता है।

इसके अलावा, डेल्फी 2010 से शुरू होने पर मेरा मानना ​​है कि, टॉब्जेक्ट ने 4 के बजाय 8 बाइट्स का उपयोग करना शुरू किया। इसलिए डेल्फी द्वारा बनाई गई प्रत्येक एकल वस्तु के लिए, डेल्फी अब 4 और बाइट्स का उपयोग करता है। उन 4 बाइट्स को टीएमओनिटर कक्षा का समर्थन करने के लिए जोड़ा गया था।

यदि आपको मेमोरी को बचाने की बेहद जरूरी ज़रूरत है, तो यहां एक छोटी सी चाल है जो आपकी मदद कर सकती है यदि आपके पास बहुत सारे स्ट्रिंग मान हैं जो themselve को दोहराते हैं।

var 
    uUniqueStrings : TStringList; 

function ReduceStringMemory(const S : String) : string; 
var idx : Integer; 
begin 
    if not uUniqueStrings.Find(S, idx) then 
    idx := uUniqueStrings.Add(S); 

    Result := uUniqueStrings[idx] 
end; 

ध्यान दें यह केवल मदद करेगा अगर आप स्ट्रिंग मान जो स्वयं को दोहराने की एक बहुत कुछ है। उदाहरण के लिए, यह कोड मेरे सिस्टम पर 150 एमबी कम का उपयोग करता है।

var sl : TStringList; 
    I: Integer; 
begin 
    sl := TStringList.Create; 
    try 
    for I := 0 to 5000000 do 
     sl.Add(ReduceStringMemory(StringOfChar('A',5)));every 
    finally 
    sl.Free; 
    end; 
end; 
+0

मैंने अभी इसे चेक किया है: 'TObject.Create.InstanceSize = 4' डेल्फी 2010 से पहले - यह क्लास प्रकार के लिए केवल सूचक है। –

+0

लेकिन एक्सई और 2010 में यह 8 है: वीएमटी पॉइंटर और मॉनिटर फ़ील्ड। –

+1

@ रूडी बिल्कुल। एक काम नहीं कर रहे मॉनिटर क्षेत्र के लिए बहुत कुछ। ट्रोलिंग का अंत ;) –

1

मैंने अपने कार्यक्रम में कई तारों में भी पढ़ा है जो बड़ी फ़ाइलों के लिए कुछ जीबी तक पहुंच सकते हैं।

मैं एक stringlist में अलग-अलग तार भंडारण धीमी और स्मृति के मामले में बेकार हो पाया:

64-बिट XE2 के लिए इंतजार की कमी है, यहाँ एक विचार है कि आपकी मदद कर सकता है। मैं तारों को एक साथ अवरुद्ध कर दिया। मेरी इनपुट फ़ाइल में लॉजिकल रिकॉर्ड्स हैं, जिनमें 5 और 100 लाइनें हो सकती हैं। तो स्ट्रिंगलिस्ट में प्रत्येक पंक्ति को संग्रहीत करने के बजाय, मैं प्रत्येक रिकॉर्ड स्टोर करता हूं। जिस लाइन को मुझे चाहिए, उसे ढूंढने के लिए एक रिकॉर्ड को प्रोसेस करना मेरे प्रसंस्करण में बहुत कम समय जोड़ता है, इसलिए यह मेरे लिए संभव है।

यदि आपके पास तार्किक रिकॉर्ड नहीं हैं, तो आप केवल एक अवरुद्ध आकार चुनना चाहते हैं, और प्रत्येक स्ट्रिंग के रूप में 10 (100) 10 या 100 तारों को एक स्ट्रिंग के रूप में स्टोर कर सकते हैं (एक डिलीमीटर उन्हें अलग करके)।

दूसरा विकल्प, उन्हें एक तेज़ और कुशल ऑन-डिस्क फ़ाइल में स्टोर करना है। जिसकी मैं सिफारिश करता हूं वह ओपन सोर्स Synopse Big TableArnaud Bouchez है।

+0

ओपन सोर्स प्रोजेक्ट्स के लिए आपके प्रचार के लिए धन्यवाद! –

3

टिप्पणियों के बावजूद पढ़ना, ऐसा लगता है कि आपको डेल्फी से डेटा को डेटाबेस में उठाना होगा और डेटाबेस में ले जाना होगा।

वहां से यह आप रोगियों है कि नए अंग-दाता 15484.

के खिलाफ मेल करने वाले स्मृति आप केवल संभाल में देखना चाहते हैं तो * रिसीवरों को अंग दाताओं)

SELECT pw.* FROM patients_waiting pw 
INNER JOIN organs_available oa ON (pw.bloodtype = oa.bloodtype) 
           AND (pw.tissuetype = oa.tissuetype) 
           AND (pw.organ_needed = oa.organ_offered) 
WHERE oa.id = '15484' 

मिलान करने के लिए आसान है मिलान करने वाले कुछ रोगी।

*) सभी मान्यताओं से परे सरलीकृत, लेकिन फिर भी।

+2

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

10

बस एक विचार जो स्मृति को बचा सकता है।

आप डेटा को मूल फ़ाइलों पर रहने दे सकते हैं, फिर बस इन-मेमोरी संरचनाओं से उन्हें इंगित करें।

उदाहरण के लिए, यह है कि हम क्या browsing big log files almost instantly के लिए करते हैं: हम लॉग फ़ाइल सामग्री स्मृति-मैप करते हैं, तो हम इसे जल्दी स्मृति में उपयोगी जानकारी के अनुक्रमणिका बनाने के लिए पार्स, तो हम सामग्री गतिशील पढ़ें। पढ़ने के दौरान कोई स्ट्रिंग नहीं बनाई गई है। आवश्यक इंडेक्स युक्त गतिशील सरणी के साथ, प्रत्येक पंक्ति के लिए केवल पॉइंटर्स। कॉलिंग TStringList.LoadFromFile निश्चित रूप से धीमी और स्मृति उपभोग होगी।

कोड here है - TSynLogFile कक्षा देखें। यह चाल केवल फाइल को पढ़ने के लिए है, और सभी इंडेक्स को फ्लाई पर बनाना है।

उदाहरण के लिए, यहाँ है कि हम कैसे UTF-8 फ़ाइल सामग्री से पाठ की एक पंक्ति को पुनः प्राप्त:

function TMemoryMapText.GetString(aIndex: integer): string; 
begin 
    if (self=nil) or (cardinal(aIndex)>=cardinal(fCount)) then 
    result := '' else 
    result := UTF8DecodeToString(fLines[aIndex],GetLineSize(fLines[aIndex],fMapEnd)); 
end; 

हम parse JSON content को ठीक उसी चाल का उपयोग करें। इस तरह के मिश्रित दृष्टिकोण का उपयोग by the fastest XML access libraries का उपयोग किया जाता है।

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

तो आपके पास कोई भी डुप्लीकेट संरचना नहीं होगी, केवल रैम में तर्क होगा, और मेमोरी-मैप किए गए फ़ाइल (ओं) पर डेटा - मैंने यहां "एस" जोड़ा क्योंकि एक ही तर्क कई स्रोत डेटा को पूरी तरह से मानचित्र कर सकता है फाइलें (आपको कुछ "मर्ज" और "लाइव रीफ्रेश" AFAIK की आवश्यकता है)।

0

मई मेरा सुझाव है कि आप जेडीआई क्लास लाइब्रेरी (जेसीएल) क्लास टीएन्सीस्ट्रिंगलिस्ट का उपयोग करने का प्रयास करें, जो कि टेल्स्ट्रिंगलिस्ट से डेल्फी 2007 की तरह है, जिसमें यह AnsiStrings से बना है।

फिर भी, के रूप में दूसरों का उल्लेख किया है, XE डेल्फी की तुलना में अधिक स्मृति का उपयोग किया जाएगा 2007

मैं वास्तव में एक stringlist में एक विशाल फ्लैट फ़ाइल का पूरा पाठ को लोड करने की मूल्य नहीं दिख रहा। अन्य ने अर्नुद बौचेज़ के एक बड़े दृष्टिकोण, या स्क्लाइट का उपयोग करने, या ऐसा कुछ करने का सुझाव दिया है, और मैं उनके साथ सहमत हूं।

मुझे लगता है कि आप एक साधारण कक्षा भी लिख सकते हैं जो आपके पास स्मृति में मौजूद पूरी फ़ाइल को लोड करेगी, और एक विशाल इन-मेमोरी ansichar बफर के लिए लाइन-बाय-लाइन ऑब्जेक्ट लिंक जोड़ने का एक तरीका प्रदान करेगी।

0

डेल्फी 200 से शुरू, न केवल स्ट्रिंग्स बल्कि प्रत्येक टॉब्जेक्ट आकार में दोगुना हो गया है। (Why Has the Size of TObject Doubled In Delphi 2009? देखें)। लेकिन अगर यह केवल 85,000 वस्तुओं हैं तो यह वृद्धि में वृद्धि नहीं होगी। केवल अगर इन वस्तुओं में कई घोंसले वाली वस्तुएं होती हैं, तो उनका आकार स्मृति उपयोग का एक प्रासंगिक हिस्सा हो सकता है।

+0

उनके पास हैं और वे हैं: _ समस्या समस्या हो सकती है, उनके पास लगभग 60 फ़ील्ड हैं, जिनमें से लगभग 10 सूचियां हैं। अधिकांश सूचियां छोटी हैं लेकिन विशेष रूप से 200 फ़ील्ड long_ है। – NGLN

0

क्या आपकी सूची में कई डुप्लिकेट स्ट्रिंग हैं? शायद अद्वितीय तारों को स्टोर करने की कोशिश करने से स्मृति आकार को कम करने में मदद मिलेगी। एक संभावित (लेकिन शायद बहुत सरल) उत्तर के लिए मेरा प्रश्न about a string pool देखें।

0

क्या आप वाकई मेमोरी सगाई के मामले से पीड़ित नहीं हैं?

नवीनतम FastMM (वर्तमान में 4.9 7) का उपयोग करना सुनिश्चित करें, फिर UsageTrackerDemo डेमो पर एक नज़र डालें जिसमें मेमोरी मेमोरी का वास्तविक उपयोग दिखाए गए मेमोरी मैप फॉर्म शामिल हैं।

अंत में VMMap पर एक नज़र डालें जो आपको दिखाता है कि आपकी प्रक्रिया मेमोरी का उपयोग कैसे किया जाता है।

संबंधित मुद्दे