Почему мой код такой медленный? - PullRequest
5 голосов
/ 22 января 2011

Топ-ответ (извините), для тех, кто не успевает зайти в него, но может иметь схожие проблемы.

Правило № 1, как всегда, отодвинется как можно большециклы.
2, перемещение TField var: = ADODataSet.FieldByname () из цикла 3, ADODataSet.DisableControls ();и ADODataSet.EnableControls ();Вокруг цикла 4, stringGrid.Rows [r] .BeginUpdate () и EndUpdate () в каждой строке (не может работать с этим элементом управления), каждый из них сбрасывается на несколько секунд, , но Я получил его«быстрее, чем видит глаз», изменив

loop
  stringGrid.RowCount := stringGrid.RowCount + 1;
end loop

на stringGrid.RowCount := ADODataSet.RecordCount; перед циклом

+ 1 и сердечно благодарим всех, кто помог.

(сейчас я пойду и посмотрю, что я могу сделать, чтобы оптимизировать рисование TChart, что тоже медленно; -)


с примерно 3600 строками в таблице, для заполнения строковой сетки требуется 45 секунд.Что я делаю не так?

   ADODataSet := TADODataSet.Create(Nil);
   ADODataSet.Connection := AdoConnection;

   ADODataSet.CommandText := 'SELECT * FROM measurements';
   ADODataSet.CommandType := cmdText;
   ADODataSet.Open();

   while not ADODataSet.eof do
   begin
      TestRunDataStringGrid.RowCount := TestRunDataStringGrid.RowCount + 1;

      measurementDateTime   := UnixToDateTime(ADODataSet.FieldByname('time_stamp').AsInteger);
      DoSQlCommandWithResultSet('SELECT * FROM start_time_stamp', AdoConnection, resultSet);
      startDateTime := UnixToDateTime(StrToInt64(resultSet.Strings[0]));
      elapsedTime   := measurementDateTime - startDateTime;
      TestRunDataStringGrid.Cells[0, Pred(TestRunDataStringGrid.RowCount)] := FormatDateTime('hh:mm:ss', elapsedTime);
      TestRunDataStringGrid.Cells[1, Pred(TestRunDataStringGrid.RowCount)] := FloatToStrWithPrecision(ADODataSet.FieldByname('inputTemperature').AsFloat);
      TestRunDataStringGrid.Cells[2, Pred(TestRunDataStringGrid.RowCount)] := FloatToStrWithPrecision(ADODataSet.FieldByname('outputTemperature').AsFloat);
      TestRunDataStringGrid.Cells[3, Pred(TestRunDataStringGrid.RowCount)] := FloatToStrWithPrecision(ADODataSet.FieldByname('flowRate').AsFloat);
      TestRunDataStringGrid.Cells[4, Pred(TestRunDataStringGrid.RowCount)] := FloatToStrWithPrecision(ADODataSet.FieldByname('waterPressure').AsFloat * convert);
      TestRunDataStringGrid.Cells[5, Pred(TestRunDataStringGrid.RowCount)] := FloatToStrWithPrecision(ADODataSet.FieldByname('waterLevel').AsFloat);
      TestRunDataStringGrid.Cells[6, Pred(TestRunDataStringGrid.RowCount)] := FloatToStrWithPrecision(ADODataSet.FieldByname('cod').AsFloat);
      ADODataSet.Next;
   end;

   ADODataSet.Close();
   ADODataSet.Free();

обновление:

Function  DoSQlCommandWithResultSet(const command : String; AdoConnection : TADOConnection; resultSet : TStringList): Boolean;
  var
        i : Integer;
        AdoQuery : TADOQuery;

begin
  Result := True;
  resultSet.Clear();

  AdoQuery := TADOQuery.Create(nil);
  try
    AdoQuery.Connection := AdoConnection;
    AdoQuery.SQL.Add(command);
    AdoQuery.Open();
    i := 0;
    while not  AdoQuery.eof do
    begin
      resultSet.Add(ADOQuery.Fields[i].Value);
      i := i + 1;
      AdoQuery.Next;
    end;

  finally
    AdoQuery.Close();
    AdoQuery.Free();
  end;
end;

Ответы [ 6 ]

10 голосов
/ 22 января 2011
  1. Вы выполняете команду SELECT * FROM start_time_stamp 3600 раз, но мне не кажется, что она каким-либо образом связана с вашим внешним циклом. Почему бы не выполнить его один раз перед циклом?

  2. Эта команда SELECT, по-видимому, возвращает только один столбец одной записи, но вы используете "*" для загрузки всех столбцов, а не предложение WHERE, чтобы ограничить результаты одной строкой (если их несколько строка в таблице).

  3. Вы используете только ограниченное количество столбцов из измерений, но извлекаете все столбцы с "*".

  4. Вы не показываете содержимое DoSQlCommandWithResultSet, поэтому неясно, есть ли проблема в этой процедуре.

  5. Непонятно, связана ли проблема с доступом к вашей базе данных или сеткой строк. Закомментируйте все строки, относящиеся к сетке строк, и запустите программу. Сколько времени занимает только доступ к базе данных?

9 голосов
/ 22 января 2011

Дополнительно к очкам Ларри Люстига:

  1. В общем, FieldByName является сравнительно медленным методом. Вы вызываете это в цикле для тех же полей. Переместите получение ссылок на поля из цикла и сохраните ссылки в переменных. Нравится: InputTempField := ADODataSet.FieldByname('inputTemperature');
  2. Вы изменяете размер сетки в цикле TestRunDataStringGrid.RowCount := TestRunDataStringGrid.RowCount + 1. Это тот случай, когда вы должны использовать ADODataSet.RecordCount перед циклом: TestRunDataStringGrid.RowCount := ADODataSet.RecordCount.
  3. Хорошей практикой является вызов ADODataSet.DisableControls перед циклом и ADODataSet.EnableControls после цикла. Еще более актуально это для набора данных ADO, который не имеет оптимальной реализации, и эти вызовы помогают.
  4. В зависимости от используемой СУБД, вы можете улучшить производительность выборки, установив больший «размер набора строк». Не уверен, как это контролировать в ADO, вероятно, установив ADODataSet.CacheSize в большее значение, поможет. Также есть настройки курсора:)
5 голосов
/ 22 января 2011

вместо вызова ADODataSet.FieldByname ('Fieldname') внутри цикла вы должны объявить локальные переменные типа TField для каждого поля, присвоить ADODataset.FindField ('Fieldname') переменным и использовать переменные внутри цикла. FindFieldByName выполняет поиск в списке при каждом вызове.

Обновление:

procedure TForm1.Button1Click(Sender: TObject);
var
  InputTemp, OutputTemp: TField;
begin
  ADODataSet := TADODataSet.Create(Nil);
  try
    ADODataSet.Connection := ADOConnection;
    ADODataSet.CommandText := 'SELECT * FROM measurements';
    ADODataSet.Open;
    InputTemp := ADODataSet.FindField('inputTemperature');
    OutputTemp := ADODataSet.FindField('outputTemperature');
    // assign more fields here
    while not ADODataSet.Eof do begin
      // do something with the fields, for example:
      // GridCell := Format ('%3.2f', [InputTemp.AsFloat]);
      // GridCell := InputTemp.AsString;
      ADODataSet.Next;
    end;
  finally
    ADODataSet.Free;
  end;
end;

Другим вариантом может быть удаление компонента TADODataset на форме (или использование модуля TDataModule) и определение полей во время разработки.

2 голосов
/ 22 января 2011

Если вы не используете элементы управления с учетом данных, вы должны использовать TestRunDataStringGrid.BeginUpdate до и TestRunDataStringGrid.EndUpdate после цикла. Без этого ваша сетка будет постоянно перерисовываться после каждой модификации (добавление новой строки, обновление ячейки).

Еще один совет устанавливается AdoQuery.LockType := ltReadOnly перед открытием запроса.

2 голосов
/ 22 января 2011

Дополнительно к ответу Ларри Лустига, рассмотрите возможность использования вместо них элементов управления data-aware, таких как alt text TDbGrid.

1 голос
/ 24 января 2011

Вы также можете попробовать инструментальный профилировщик вместо профилировщика выборки, чтобы получить лучшие результаты (профилировщики выборки пропускают много подробной информации, и в большинстве случаев у них меньше 1000 выборок в секунду, а 1000 уже низко: только хорошо получить краткий обзор).

Инструментальные профилировщики:

...