为什么我的代码这么慢?

时间:2011-01-22 03:11:40

标签: delphi profiling ado tstringgrid

热门(对不起)答案,对于那些没有时间进入但可能有类似问题的人。

规则#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,这也很慢; - )


表中有大约3,600行,这需要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 个答案:

答案 0 :(得分:10)

  1. 您正在执行命令SELECT * FROM start_time_stamp 3,600次,但在我看来它并不以任何方式与您的外循环相关联。为什么不在循环之前执行一次?

  2. SELECT命令似乎只返回单个记录的单个列,但是您使用“*”来加载所有列,而没有WHERE子句将结果限制为单个行(如果有多个表中的一行)。

  3. 您只使用测量中的有限数量的列,但使用“*”检索所有列。

  4. 您没有显示DoSQlCommandWithResultSet的内容,因此不清楚该例程是否存在问题。

  5. 目前尚不清楚问题是在您的数据库访问还是字符串网格中。注释掉与字符串网格相关的所有行并运行程序。单独访问数据库需要多长时间?

答案 1 :(得分:9)

除了Larry Lustig点:

  1. 一般来说,FieldByName是比较慢的方法。您在循环中为相同的字段调用它。将获取字段引用移出循环并将引用存储在变量中。喜欢:InputTempField := ADODataSet.FieldByname('inputTemperature');
  2. 您正在循环TestRunDataStringGrid.RowCount := TestRunDataStringGrid.RowCount + 1中调整网格大小。如果你应该在循环之前使用ADODataSet.RecordCountTestRunDataStringGrid.RowCount := ADODataSet.RecordCount
  3. ,就是这种情况
  4. 在循环之前调用ADODataSet.DisableControls并在循环之后调用ADODataSet.EnableControls是一个好习惯。更实际的是ADO数据集,它没有最佳实现,而且这些调用有帮助。
  5. 根据您使用的DBMS,您可以通过设置更大的“行集大小”来提高提取性能。不确定,它如何在ADO中控制,可能将ADODataSet.CacheSize设置为更大的值将有所帮助。此外,还有光标设置:)

答案 2 :(得分:5)

而不是在循环内调用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 Componont(或使用TDataModule)并在设计时定义字段。

答案 3 :(得分:2)

除了Larry Lustig的回答之外,请考虑使用data-aware控件,例如alt text TDbGrid组件。

答案 4 :(得分:2)

如果您没有使用数据感知控件,则应在循环之前使用TestRunDataStringGrid.BeginUpdate并在循环之后使用TestRunDataStringGrid.EndUpdate。如果没有这个,你的网格会在每次修改后不断重绘(添加新行,单元格更新)。

在打开查询之前,另一个提示设置为AdoQuery.LockType := ltReadOnly

答案 5 :(得分:1)

您还可以尝试使用仪器分析器而不是采样分析器来获得更好的结果(采样分析器会错过很多细节信息,而且大多数时候它们每秒的样本少于1000个样本,1000个样本已经很低了:只有很好才能获得快速概述)。

检测分析器: