2015-11-27 8 views
9

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

procedure TForm1.Button1Click(Sender: TObject); 
const 
    MaxRecords = 40000; 
    Step = 5000; 
var 
    I, J: Integer; 
    Table: TADOTable; 
    T: Cardinal; 
    Ts: TCardinalDynArray; 
begin 
    Table := TADOTable.Create(nil); 
    Table.ConnectionString := 
    'Provider=Microsoft.ACE.OLEDB.12.0;'+ 
    'Data Source=BigMDB.accdb;'+ 
    'Mode=Read|Share Deny Read|Share Deny Write;'+ 
    'Persist Security Info=False'; 
    Table.TableName := 'Table1'; 
    Table.Open; 

    J := 0; 
    SetLength(Ts, MaxRecords div Step); 
    T := GetTickCount; 
    for I := 1 to MaxRecords do 
    begin 
    Table.Next; 
    if ((I mod Step) = 0) then 
    begin 
     T := GetTickCount - T; 
     Ts[J] := T; 
     Inc(J); 
     T := GetTickCount; 
    end; 
    end; 
    Table.Free; 

// Chart1.SeriesList[0].Clear; 
// for I := 0 to Length(Ts) - 1 do 
// begin 
// Chart1.SeriesList[0].Add(Ts[I]/1000, Format(
//  'Records: %s %d-%d %s Duration:%f s', 
//  [#13, I * Step, (I + 1)*Step, #13, Ts[I]/1000])); 
// end; 
end; 

और मेरे पीसी पर परिणाम:: यहाँ एक नमूना कोड है कि इस दर्शाता है enter image description here

तालिका में दो स्ट्रिंग क्षेत्रों, एक डबल और एक पूर्णांक है। इसमें कोई प्राथमिक कुंजी और न ही इंडेक्स फ़ील्ड है। ऐसा क्यों होता है और मैं इसे कैसे रोक सकता हूं?

+0

नहीं। मैं प्रोग्रामेटिक रूप से नियंत्रण बना रहा हूं, नमूना कोड में आप जो देख सकते हैं उससे कहीं ज्यादा कुछ नहीं है। – saastn

+0

क्या आपके लिए लूप बंद नहीं है? वैसे भी, क्या आप हैरान हैं कि यदि आप बहुत सारे रिकॉर्ड पढ़ते हैं, तो इसमें बहुत सारी मेमोरी आवंटन शामिल है, और इन्हें अधिक स्मृति आवंटित करने में अधिक समय लगता है? – MartynA

+0

@ मार्टिनए आप लूप के बारे में सही हैं। लेकिन मैं यह नहीं कह सकता कि यह स्मृति आवंटन है जो इसे धीमा कर देता है। ऐसा लगता है कि यह 'टेबल' ओपन 'में सभी रिकॉर्ड्स प्राप्त करता है, टास्क मैनेजर उस लाइन को चलाने के बाद कोई मेमोरी आवंटन नहीं दिखाता है। – saastn

उत्तर

17

मैं आपके परिणामों को एक समान आकार के एमएस एसक्यूएल सर्वर डेटासेट के साथ एक AdoQuery का उपयोग कर पुन: पेश कर सकता हूं।

हालांकि, कुछ लाइन-प्रोफाइलिंग करने के बाद, मुझे लगता है कि मुझे इसका जवाब मिल गया है, और यह थोड़ा सा अंतर्ज्ञानी है। मुझे यकीन है कि डेल्फी में डीबी प्रोग्रामिंग का उपयोग इस विचार के लिए किया जाता है कि डेटासेट के माध्यम से लूपिंग बहुत तेज हो जाती है यदि आप अक्षम/सक्षम नियंत्रणों पर कॉल द्वारा लूप को घेरते हैं। लेकिन डेटासेट से जुड़े डीबी-जागरूक नियंत्रण नहीं होने पर कौन ऐसा करने के लिए परेशान होगा?

ठीक है, यह पता चला है कि आपकी स्थिति में, भले ही कोई डीबी-जागरूक नियंत्रण न हो, फिर भी यदि आप अक्षम/सक्षम नियंत्रणों का उपयोग करते हैं तो गति बहुत बढ़ जाती है।

 if ControlsDisabled then 
     RecordNumber := -2 else 
     RecordNumber := Recordset.AbsolutePosition; 

और मेरी लाइन प्रोफाइलर के अनुसार, जबकि AdoQuery1.Next पाश नहीं AdoQuery1.Eof अपने समय से 98.8% को क्रियान्वित करने में खर्च करता है:

कारण AdoDB.Pas में TCustomADODataSet.InternalGetRecord इस होता है वह यह है कि काम

 RecordNumber := Recordset.AbsolutePosition; 

! Recordset.AbsolutePosition की गणना, निश्चित रूप से, रिकॉर्ड्स इंटरफ़ेस के "गलत पक्ष" पर छिपी हुई है, लेकिन तथ्य यह है कि इसे कॉल करने का समय स्पष्ट रूप से आगे बढ़ता है जब आप रिकॉर्डसेट में जाते हैं तो यह अनुमान लगाता है कि इसकी गणना की गई है रिकॉर्डसेट के डेटा की शुरुआत से गिनती करके।

बेशक, ControlsDisabled को कॉल किया गया है और EnableControls पर कॉल द्वारा पूर्ववत नहीं किया गया है। तो, अक्षम/सक्षम नियंत्रण से घिरे लूप के साथ पुनः प्रयास करें और उम्मीद है कि आपको मेरा एक ही परिणाम मिल जाएगा। ऐसा लगता है कि आप सही थे कि मंदी स्मृति आवंटन से संबंधित नहीं है।

निम्नलिखित कोड का उपयोग करना:

procedure TForm1.btnLoopClick(Sender: TObject); 
var 
    I: Integer; 
    T: Integer; 
    Step : Integer; 
begin 
    Memo1.Lines.BeginUpdate; 
    I := 0; 
    Step := 4000; 
    if cbDisableControls.Checked then 
    AdoQuery1.DisableControls; 
    T := GetTickCount; 
{.$define UseRecordSet} 
{$ifdef UseRecordSet} 
    while not AdoQuery1.Recordset.Eof do begin 
    AdoQuery1.Recordset.MoveNext; 
    Inc(I); 
    if I mod Step = 0 then begin 
     T := GetTickCount - T; 
     Memo1.Lines.Add(IntToStr(I) + ':' + IntToStr(T)); 
     T := GetTickCount; 
    end; 
    end; 
{$else} 
    while not AdoQuery1.Eof do begin 
    AdoQuery1.Next; 
    Inc(I); 
    if I mod Step = 0 then begin 
     T := GetTickCount - T; 
     Memo1.Lines.Add(IntToStr(I) + ':' + IntToStr(T)); 
     T := GetTickCount; 
    end; 
    end; 
{$endif} 
    if cbDisableControls.Checked then 
    AdoQuery1.EnableControls; 
    Memo1.Lines.EndUpdate; 
end; 

निम्न परिणाम (DisableControls साथ अपवाद का उल्लेख नहीं बुलाया नहीं) मैं हो:

Using CursorLocation = clUseClient 

AdoQuery.Next AdoQuery.RecordSet AdoQuery.Next 
       .MoveNext    + DisableControls 

4000:157   4000:16    4000:15 
8000:453   8000:16    8000:15 
12000:687   12000:0    12000:32 
16000:969   16000:15   16000:31 
20000:1250   20000:16   20000:31 
24000:1500   24000:0    24000:16 
28000:1703   28000:15   28000:31 
32000:1891   32000:16   32000:31 
36000:2187   36000:16   36000:16 
40000:2438   40000:0    40000:15 
44000:2703   44000:15   44000:31 
48000:3203   48000:16   48000:32 

======================================= 

Using CursorLocation = clUseServer 

AdoQuery.Next AdoQuery.RecordSet AdoQuery.Next 
       .MoveNext    + DisableControls 

4000:1031   4000:454   4000:563 
8000:1016   8000:468   8000:562 
12000:1047   12000:469   12000:500 
16000:1234   16000:484   16000:532 
20000:1047   20000:454   20000:546 
24000:1063   24000:484   24000:547 
28000:984   28000:531   28000:563 
32000:906   32000:485   32000:500 
36000:1016   36000:531   36000:578 
40000:1000   40000:547   40000:500 
44000:968   44000:406   44000:562 
48000:1016   48000:375   48000:547 

MDAC/ADO परत में सीधे AdoQuery1.Recordset.MoveNext कॉल कॉलिंग , पाठ्यक्रम, जबकि AdoQuery1.Next मानक TDataSet मॉडल के सभी ओवरहेड शामिल है। सर्ज क्राइकोव ने कहा, कर्सरोकोकेशन को बदलना निश्चित रूप से एक फर्क पड़ता है और हमने जो मंदी देखी है, वह प्रदर्शित नहीं करती है, हालांकि स्पष्ट रूप से यह क्लाउसे क्लाइंट का उपयोग करने और अक्षम कॉन्ट्रोल को कॉल करने से काफी धीमी है। मुझे लगता है कि यह वास्तव में निर्भर करता है कि आप क्या करने की कोशिश कर रहे हैं कि क्या आप RecordSet.MoveNext के साथ clUseClient का उपयोग करने की अतिरिक्त गति का लाभ उठा सकते हैं।

+0

आपको बहुत धन्यवाद, 'अक्षम कॉन्ट्रोल' ने मेरे लिए काम किया। लेकिन, आपके परिणामों के विपरीत, 'clUseServer' यहां' clUseClient' से धीमा नहीं है। हालांकि, डेटासेट 'कर्सर सर्वर' को 'क्लूससेवर' पर सेट करने के बाद कोई रिकॉर्ड नहीं लौटाता है, जब तक कि मैं 'लॉकटाइप' को 'ltReadOnly' पर सेट नहीं करता। – saastn

+0

@ मार्टिनए जिज्ञासा से बाहर, आप किस प्रोफाइलर का उपयोग करते थे? –

+0

@ क्रिश्चियनहोल्म जोर्जेंसन: मैंने नेक्सस क्वालिटी सूट (www.nexusdb.com) के लाइन प्रोफाइलर का उपयोग किया जो एक समान नाम के पुराने टर्बोपावर उत्पाद का पुनर्जन्म है। – MartynA

1

जब आप कोई तालिका खोलते हैं, तो एडीओ डेटासेट आंतरिक रूप से डेटासेट आगे/पिछड़े - "डेटासेट कर्सर" नेविगेट करने के लिए विशेष डेटा संरचनाएं बनाता है। नेविगेशन के दौरान, एडीओ बिडरेक्शनल नेविगेशन प्रदान करने के लिए पहले से देखे गए रिकॉर्ड की सूची संग्रहीत करता है।
लगता है कि एडीओ कर्सर कोड इस सूची को स्टोर करने के लिए क्वाड्रैटिक-टाइम ओ (एन 2) एल्गोरिदम का उपयोग करता है।
लेकिन वहाँ वैकल्पिक हल कर रहे हैं - उपयोग सर्वर साइड कर्सर:

Table.CursorLocation := clUseServer; 

मैं इसे ठीक उपयोग करते हुए और रैखिक मिल समय लाने के लिए अपने कोड का परीक्षण किया - रिकॉर्ड के हर अगले हिस्सा प्राप्त करते समय पिछले रूप में एक ही समय लगता है।

पीएस कुछ अन्य डेटा एक्सेस लाइब्रेरी विशेष "यूनिडायरेक्शनल" डेटासेट प्रदान करते हैं - यह डेटासेट केवल आगे बढ़ सकता है और पहले से ही ट्रैवर्स किए गए रिकॉर्ड्स को स्टोर भी नहीं कर सकता - आपको निरंतर स्मृति खपत और रैखिक fetch समय मिलता है।

1

डीएओ एक्सेस के मूल निवासी है और (आईएमएचओ) आमतौर पर तेज़ है। चाहे आप स्विच करें या नहीं, GetRows विधि का उपयोग करें। डीएओ और एडीओ दोनों इसका समर्थन करते हैं। कोई लूपिंग नहीं है। आप पूरे रिकॉर्डसेट को कोड की दो पंक्तियों के साथ एक सरणी में डंप कर सकते हैं। वायु कोड: yourrecordset.MoveLast yourrecordset.MoveFirst yourarray = yourrecordset.GetRows(yourrecordset.RecordCount)

+0

शायद, लेकिन ओपी डेल्फी कोड के बारे में पूछ रहा है, और डेल्फी में, आप आमतौर पर डीबी रिकॉर्ड के सरणी का काम नहीं करते हैं। – MartynA

+0

धन्यवाद मार्टिनिया। मुझे डेल्फी के बारे में कुछ नहीं पता, लेकिन सिर्फ सोचा कि इसमें अन्य भाषाओं के समान संरचनाएं हो सकती हैं। – AVG

+0

ठीक है, यह * उन्हें * कर सकता है (केवल उपयुक्त प्रकार की सरणी घोषित करके) लेकिन यह चीजों को करने का "डेल्फी" तरीका नहीं है। बिंदु, डेल्फी में, सभी समर्थित डेटासेट प्रकार एक पूर्वजों (टीडीसेटसेट) के वंशज हैं जिनमें चलने वाले लॉजिकल कर्सर वाले डेटासेट का एक सामान्यीकृत मॉडल शामिल है। और इसके सभी डीबी-जागरूक नियंत्रणों को सरणी के बजाए इस मॉडल के साथ संवाद करने के लिए डिज़ाइन किया गया है। नतीजा यह है कि इसके सभी डीबी-जागरूक नियंत्रण किसी भी समर्थित टीडीसेटसेट वंश के साथ काम करते हैं। – MartynA

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