2011-01-22 34 views
5

Respuesta publicada (lo siento), para aquellos que no tienen tiempo para entrar pero que pueden tener problemas similares.¿Por qué mi código es tan lento?

Regla # 1, como siempre, mueva todo lo que pueda fuera de los bucles.
2, moviendo TField var: = ADODataSet.FieldByname() fuera del ciclo 3, ADODataSet.DisableControls(); y ADODataSet.EnableControls(); alrededor del ciclo 4, stringGrid.Rows [r] .BeginUpdate() y EndUpdate() en cada fila (no se puede hacer en el control) cada uno de estos se cortó unos segundos, pero Lo tengo abajo " más rápido que el ojo puede ver" cambiando

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

a poner stringGrid.RowCount := ADODataSet.RecordCount; antes del bucle

1 y sincero agradecimiento a todos los que ayudaron.

(Ahora voy a ir a ver lo que puedo hacer para optimizar la elaboración de un TChart, que también es lento ;-)


con cerca de 3.600 filas en la tabla de esto toma 45 segundos para poblar la cadena cuadrícula. ¿Qué estoy haciendo mal?

 
    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(); 

actualización:

 
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; 

+0

La mejor manera de acercarse a la respuesta es al perfil de alguna manera su código. Además, no hay idea de cuántas filas debe devolver 'select * from time_stamp'; tal vez es un trillón de filas? – 9000

+0

+1 Gracias. ¿Alguna recomendación sobre el perfil? En este caso, estaba tomando medidas a intervalos de un segundo durante aproximadamente una hora (más/menos unos segundos) – Mawg

+1

Debe probar SamplingProfiler, disponible en http://delphitools.info/samplingprofiler. Le da una buena visión de lo que su programa está pasando tiempo, funciona bien con las aplicaciones Delphi, y es gratis. :) –

Respuesta

9

Adicionalmente a Larry Lustig puntos:

  1. En general, FieldByName es comparativamente método lento. Lo está llamando en bucle para los mismos campos. Mueva la obtención de referencias de campo fuera del ciclo y almacene referencias en las variables. Me gusta: InputTempField := ADODataSet.FieldByname('inputTemperature');
  2. Está cambiando el tamaño de la cuadrícula en el bucle TestRunDataStringGrid.RowCount := TestRunDataStringGrid.RowCount + 1. Ese es el caso, cuando debe usar ADODataSet.RecordCount antes del ciclo: TestRunDataStringGrid.RowCount := ADODataSet.RecordCount.
  3. Es una buena práctica llamar al ADODataSet.DisableControls antes del ciclo y ADODataSet.EnableControls después del ciclo. Aún más real es para el conjunto de datos ADO, que no tiene una implementación óptima y esas llamadas ayudan.
  4. Dependiendo de un DBMS que esté utilizando, puede mejorar el rendimiento de búsqueda configurando un "tamaño de conjunto de filas" más grande. No estoy seguro, cómo controla en ADO, probablemente ajuste ADODataSet.CacheSize a un valor mayor. Además, hay una configuración de cursor :)
+0

+1 Gracias. # 1 y 2 son definitivamente buenas ideas (¿estás diciendo que InputTempField es un TField y uso InputTempField.AsFloat en el ciclo?). Ya tomé el # 3 a bordo. Para el n. ° 4, soy un poco n00b, así que lo dejaré para más adelante. Además, debe ser demandante de ODBC. – Mawg

+0

Sí InputTempField: TField. –

+0

TestRunDataStringGrid.RowCount: = ADODataSet.RecordCount; resultó para hacer la diferencia. Algunos otros ayudan, pero ese realmente lo hizo. Vea la pregunta actualizada para obtener más información – Mawg

10
  1. Usted está ejecutando el comando SELECT * FROM start_time_stamp 3.600 veces, pero no me parece que se correlaciona con su lazo externo de ninguna manera. ¿Por qué no ejecutarlo una vez antes del ciclo?

  2. Ese comando SELECCIONAR parece devolver solo una columna de un solo registro, sin embargo, utiliza "*" para cargar todas las columnas y ninguna cláusula WHERE para limitar los resultados a una sola fila (si hay más de una fila en la mesa).

  3. Utiliza solo un número limitado de columnas de Medidas, pero recupera todas las columnas con "*".

  4. No muestra el contenido de DoSQlCommandWithResultSet, por lo que no está claro si hay algún problema en esa rutina.

  5. No está claro si el problema está en el acceso a su base de datos o en la cuadrícula de cadenas. Comente todas las líneas pertenecientes a la cuadrícula de cadenas y ejecute el programa. ¿Cuánto tiempo dura solo el acceso a la base de datos?

+0

+1 algunos comentarios excelentes, gracias. – Mawg

+0

Vuelva y háganos saber lo que encuentra. –

+0

+1 algunos comentarios excelentes, gracias. 1) d'oh! Tengo que mover eso antes del bucle 2) buena pinta 3) en realidad quiero acceder a todos los campos de "mediciones". En ese caso, ¿SELECCIONAR * es aceptable? 4) eso podría ser un problema. Está accediendo a la misma conexión ADO. He publicado el código de arriba. – Mawg

2

adicional a la respuesta Larry Lustig, considerar el uso de controles data-aware lugar, al igual que el componente alt textTDbGrid.

+0

+1 Gracias. Todavía buscando en Google algunos ejemplos de codificación – Mawg

5

en lugar de llamar a ADODataSet.FieldByname ('Fieldname') dentro del bucle, debe declarar las variables locales de tipo TField para cada campo, asignar ADODataset.FindField ('Fieldname') a las variables y usar las variables dentro del bucle. FindFieldByName busca una lista con cada llamada.

Actualización:

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; 

Otra opción sería la de dejar caer el TADODataset Componont en el formulario (o utilizar un TDataModule) y definir los campos en tiempo de diseño.

+0

+1 ¿No podría usar TFloatField si sé que es un flotador? Si no, ¿puedes publicar algunas líneas para que pueda ver cómo hacerlo? Gracias – Mawg

+1

código de muestra agregado –

2

Si no está utilizando los controles de reconocimiento de datos que debe usar TestRunDataStringGrid.BeginUpdate antes y después de TestRunDataStringGrid.EndUpdate bucle. Sin esto, su cuadrícula se redibuja constantemente después de cada modificación (agregando una nueva fila, actualización de celda).

Otro consejo se establece AdoQuery.LockType := ltReadOnly antes de abrir la consulta.

1

También podría probar un perfilador de instrumentos en lugar de un perfilador de muestreo para obtener mejores resultados (los perfiladores de muestreo pierden mucha información detallada, y la mayoría tienen menos de 1000 muestras por segundo, y 1000 ya es bajo: solo bueno obtener una descripción general rápida).

perfiladores instrumentar:

Cuestiones relacionadas