Mogę odtworzyć wyniki za pomocą AdoQuery przy użyciu zestawu danych MS Sql Server podobnego rozmiaru do twojego.
Jednak po zrobieniu trochę profilowania linii, myślę, że znalazłem odpowiedź na to, i to jest trochę sprzeczne z intuicją. Jestem pewien, że każdy, kto wykonuje programowanie w Delphi w wersji , jest przyzwyczajony do tego, że przechodzenie przez zbiór danych jest znacznie szybsze, jeśli otoczysz pętlę wywołaniami funkcji Disable/EnableControls. Ale kto by to zawracał sobie głowę, gdyby do zestawu danych nie dołączono elementów sterujących db-aware?
Cóż, okazuje się, że w twojej sytuacji, nawet jeśli nie ma kontroli DB-aware, prędkość wzrasta ogromnie, jeśli użyjesz Disable/EnableControls niezależnie.
Powodem jest to, że TCustomADODataSet.InternalGetRecord w AdoDB.Pas zawiera to:
if ControlsDisabled then
RecordNumber := -2 else
RecordNumber := Recordset.AbsolutePosition;
i zgodnie z moim linii profilera, póki nie AdoQuery1.Eof zrobić AdoQuery1.Next pętlę spędza 98,8% swojego czasu wykonywania zadanie
RecordNumber := Recordset.AbsolutePosition;
! Obliczanie Recordset.AbsolutePosition jest ukryte, oczywiście, po "złej stronie" interfejsu Recordset, ale fakt, że czas na wywołanie go wyraźnie zwiększa się, gdy idziesz dalej do zestawu rekordów, sprawia, że rozsądnie jest spekulować, że jest on obliczony przez odliczanie od początku danych zestawu rekordów.
Oczywiście zwraca wartość true, jeśli została wywołana DisableControls
i nie została cofnięta przez połączenie z EnableControls
. Tak więc, powtórz test z pętlą otoczoną przez Disable/EnableControls i miejmy nadzieję, że uzyskasz podobny wynik do mojego. Wygląda na to, że masz rację, że spowolnienie nie ma związku z przydzielaniem pamięci.
Korzystanie poniższy kod:
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;
uzyskać następujące wyniki (z DisableControls nie nazywa ile nie zaznaczono inaczej):
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
Wywołanie AdoQuery1.Recordset.MoveNext
połączenia bezpośrednio do warstwy MDAC/ADO z kursu , natomiast AdoQuery1.Next obejmuje cały narzut standardowego modelu TDataSet . Jak powiedział Serge Kraikov, zmiana CursorLocation z pewnością robi różnicę i nie wykazuje spowolnienia, które zauważyliśmy, chociaż oczywiście jest znacznie wolniejsza niż użycie clUseClient i wywoływanie DisableControls. Przypuszczam, że zależy to dokładnie od tego, co próbujesz zrobić, czy możesz skorzystać z dodatkowej szybkości używania clUseClient z RecordSet.MoveNext.
Nie. Programuję programowo, nie ma nic więcej niż to, co można zobaczyć w przykładowym kodzie. – saastn
Czy Twoja pętla For nie jest wyłączona? W każdym razie, czy jesteś zaskoczony, że jeśli czytasz wiele rekordów, wiąże się to z dużą ilością alokacji pamięci, a to trwa dłużej, im więcej pamięci zostanie przydzielone? – MartynA
@MartynA Masz rację co do pętli. Ale nie mogę powiedzieć, że alokacja pamięci sprawia, że jest wolniejsza. Wygląda na to, że pobiera wszystkie rekordy w 'Table.Open', Menedżer zadań nie pokazuje przydziału pamięci po uruchomieniu tej linii. – saastn