Posso riprodurre i risultati utilizzando un AdoQuery con un set di dati MS Sql Server di dimensioni simili al tuo.
Tuttavia, dopo aver eseguito un po 'di profilazione di linea, penso di aver trovato la risposta a questo, ed è leggermente contro-intuitivo. Sono sicuro che tutti coloro che eseguono la programmazione dei DB in Delphi sono abituati all'idea che il looping di un set di dati tende ad essere molto più veloce se si circonda il loop tramite chiamate a Disable/EnableControls. Ma chi si preoccuperebbe di farlo se non ci sono controlli db-aware collegati al set di dati?
Bene, si scopre che nella tua situazione, anche se non ci sono controlli compatibili con DB, la velocità aumenta notevolmente se si utilizza Disabilita/Abilita controlli indipendentemente.
La ragione è che in TCustomADODataSet.InternalGetRecord AdoDB.Pas contiene questo:
if ControlsDisabled then
RecordNumber := -2 else
RecordNumber := Recordset.AbsolutePosition;
e secondo la mia linea di profiler, il pur non AdoQuery1.Eof fare AdoQuery1.Next ciclo spende il 98,8% del suo tempo in esecuzione l'incarico
RecordNumber := Recordset.AbsolutePosition;
! Il calcolo di Recordset.AbsolutePosition è nascosto, ovviamente, sul "lato sbagliato" dell'interfaccia Recordset, ma il fatto che il tempo di chiamarlo apparentemente aumenta man mano che si va nel recordset è ragionevole ipotizzare che sia calcolato contando dall'inizio dei dati del recordset.
Ovviamente, ControlsDisabled
restituisce true se DisableControls
è stato chiamato e non annullato da una chiamata a EnableControls
. Quindi, ripeti il ciclo circondato da Disable/EnableControls e speriamo che otterrai un risultato simile al mio. Sembra che tu avessi ragione che il rallentamento non è legato alle allocazioni di memoria.
utilizzando il seguente codice:
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;
io ottenere i seguenti risultati (con DisableControls nonchiamato tranne dove indicato):
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
Calling AdoQuery1.Recordset.MoveNext
chiamate direttamente nello strato di MDAC/ADO , del corso , mentre AdoQuery1.Next riguarda tutto il sovraccarico del modello standard TDataSet . Come ha detto Serge Kraikov, cambiare CursorLocation fa certamente la differenza e non mostra il rallentamento che abbiamo notato, anche se ovviamente è molto più lento dell'utilizzo di clUseClient e chiama DisableControls. Suppongo che dipenda esattamente da cosa stai cercando di fare se puoi sfruttare la maggiore velocità di utilizzo di clUseClient con RecordSet.MoveNext.
No. Sto creando il controllo a livello di codice, non c'è niente di più di quello che puoi vedere nel codice di esempio. – saastn
Il tuo ciclo For non è di uno? Ad ogni modo, sei sorpreso che se leggi molti record, questo comporta un sacco di allocazioni di memoria, e che richiedono più tempo e più memoria viene allocata? – MartynA
@MartynA Hai ragione riguardo al ciclo. Ma non posso dire che sia l'allocazione di memoria che lo rende più lento. Sembra che recuperi tutti i record in "Table.Open', Task Manager non mostra allocazione di memoria dopo aver eseguito quella riga. – saastn