Я могу воспроизвести ваши результаты с помощью AdoQuery с набором данных MS Sql Server такого же размера, как ваш.
Однако, сделав немного профилирования строк, я думаю, что нашел ответ на этот вопрос, и он немного противоречит интуиции. Я уверен, что все, кто делает DB-программирование в Delphi, используют идею о том, что цикл через набор данных имеет тенденцию быть намного быстрее, если вы окружите цикл вызовами Disable/EnableControls. Но кто бы это сделал, если нет элементов управления db, прикрепленных к набору данных?
Хорошо, оказывается, что в вашей ситуации, даже если нет элементов управления, поддерживающих DB, скорость увеличивается очень сильно, если вы используете Disable/EnableControls независимо.
Причина заключается в том, что TCustomADODataSet.InternalGetRecord в AdoDB.Pas содержит следующее:
if ControlsDisabled then
RecordNumber := -2 else
RecordNumber := Recordset.AbsolutePosition;
и по моей линии профилировщика пока не AdoQuery1.Eof делать AdoQuery1.Next цикл тратит 98,8% своего времени выполнения присвоение
RecordNumber := Recordset.AbsolutePosition;
! Расчет Recordset.AbsolutePosition скрыт, конечно, на «неправильной стороне» интерфейса Recordset, но тот факт, что время его вызова, по-видимому, увеличивается, чем дальше вы переходите в набор записей, поэтому разумно предположить, что он рассчитан путем подсчета с начала данных набора записей.
Конечно, ControlsDisabled
возвращает true, если DisableControls
был вызван и не отменен вызовом EnableControls
. Итак, повторите попытку с циклом, окруженным Disable/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
Вызов AdoQuery1.Recordset.MoveNext
вызовов непосредственно в слой состав MDAC/ADO , , тогда как AdoQuery1.Next включает все накладные расходы стандартной модели TDataSet . Как сказал Сергей Крайев, изменение CursorLocation, безусловно, имеет значение и не показывает замедление, которое мы заметили, хотя, очевидно, это значительно медленнее, чем использование clUseClient и вызов DisableControls. Я полагаю, это зависит от того, что вы пытаетесь сделать, можете ли вы использовать дополнительную скорость использования clUseClient с помощью RecordSet.MoveNext.
Нет. Я создаю управление программно, нет ничего более того, что вы можете увидеть в примере кода. – saastn
Разве это не для вас? В любом случае, вы удивлены тем, что если вы читаете много записей, это связано с большим количеством распределений памяти, и что это занимает больше времени, чем больше выделяется память? – MartynA
@MartynA Вы правы насчет цикла. Но я не могу сказать, что это распределение памяти, которое делает его медленнее. Похоже, что он извлекает все записи в «Table.Open», диспетчер задач не отображает выделение памяти после запуска этой строки. – saastn