performance - programacion - Qué cambios simples hicieron las mayores mejoras en sus programas Delphi
partes del programa delphi (30)
- Crear pruebas unitarias
- Verificar que todas las pruebas pasen
- Perfila tu aplicación
- Refactor buscando cuellos de botella y memoria
- Repita desde el paso 2 (en comparación con el pase anterior)
Tengo un programa Delphi 2009 que maneja una gran cantidad de datos y necesita ser lo más rápido posible y no usar demasiada memoria.
¿Qué pequeños cambios simples ha realizado en su código Delphi que tuvieron el mayor impacto en el rendimiento de su programa reduciendo notablemente el tiempo de ejecución o el uso de la memoria?
Gracias a todos por todas sus respuestas. Muchos buenos consejos.
Para completar, publicaré algunos artículos importantes sobre la optimización de Delphi que encontré.
Antes de comenzar a optimizar el código Delphi en About.com
Velocidad y tamaño: los 10 mejores trucos también en About.com
Conceptos básicos de optimización de códigos y Pautas de optimización de Delphi en Delphi de alto rendimiento, relacionados con Delphi 7 pero aún muy pertinentes.
- FastMM
- FastCode (lib)
- Utilice estructuras de datos de alto rendimiento, como tabla hash (etc.). En muchos lugares es más rápido hacer un bucle que hace que la tabla hash de búsqueda para sus datos. Utiliza bastante memoria, pero seguramente es rápido. (Esto tal vez sea el más importante, pero 2 primero son simples y necesitan muy poco esfuerzo para hacerlo)
.BeginUpdate;
.EndUpdate;
;)
Al identificar registros, use enteros si es posible para la comparación de registros. Si bien una clave principal del "nombre de la compañía" puede parecer lógica, el tiempo dedicado a generar y almacenar un hash de esto mejorará en gran medida los tiempos generales de búsqueda.
Antes de hacer nada, identifique las partes lentas. No toque el código de trabajo que funciona lo suficientemente rápido.
Aproveche algunos de los códigos del proyecto FastCode . Algunas partes se incorporaron a VCL / RTL propiamente dicha (como FastMM), ¡pero hay más cosas que puedes usar!
Tenga en cuenta que también tienen un nuevo sitio en el que se están moviendo, pero parece estar un poco inactivo.
Considera los problemas de hardware. Si realmente necesita rendimiento, considere el tipo de disco duro en el que se ejecutan su programa y sus bases de datos. Hay muchas variables, especialmente si está ejecutando una base de datos. RAID no siempre es la mejor respuesta tampoco.
Considere el uso cuidadoso de los hilos. Si no está usando hilos ahora, entonces considere agregar un par. Si es así, asegúrese de no utilizar demasiados. Si está ejecutando en una computadora de núcleo dual o cuádruple (que la mayoría son más), la correcta afinación de hilos es muy importante.
Puede mirar la Biblioteca OmniThread de Gabr , pero hay una serie de bibliotecas de subprocesos en desarrollo para Delphi. Podría implementar fácilmente su propio paralelo para usar tipos anónimos.
Considere si una base de datos DBMS es realmente la elección perfecta. Si solo está leyendo datos y nunca los cambia, entonces un archivo de registro fijo plano podría funcionar más rápido, especialmente si la ruta de acceso a los datos se puede asignar fácilmente (es decir, un índice). Una búsqueda binaria trivial en un archivo de registro fijo es aún extremadamente rápida.
Cuando trabaje con una lista de tstring (o similar), configure "sorted: = false" hasta que sea necesario (si es que lo hace). Parece una obviedad...
Deja de usar TStringList para todo.
TStringList no es una estructura de datos de propósito general para el almacenamiento y manejo efectivos de todo, desde tipos simples a complejos. Busque alternativas. Uso Delphi Container y Algorithm Library (DeCAL, anteriormente conocida como SDL). Julians EZDSL también debería ser una buena alternativa.
Desactive el rango y la verificación de desbordamiento después de haber probado extensamente.
Examine todos los bucles y busque formas de cortocircuitar. Si buscas algo específico y lo encuentras en un bucle, utiliza el comando BREAK para fianza inmediata ... sin sentido bucle a través del resto. Si sabe que no tiene una coincidencia, utilice un CONTINUAR lo más rápido posible.
Haga un uso inteligente de SetLength () para cadenas y matrices. Optimizar la inicialización con FillChar o ZeroMemory.
Las variables locales creadas en la pila (p. Ej. Tipos de registros) son más rápidas que las asignadas al montón (objetos y variables Nuevas ()).
Reutiliza objetos en lugar de Destruir y luego crea. ¡Pero asegúrese de que el código de administración para esto sea más rápido que el administrador de memoria!
La mayor mejora se produjo cuando comencé a usar AsyncCalls para convertir aplicaciones de subproceso único que solían congelar la interfaz de usuario, en (tipo de) aplicaciones de subprocesos múltiples.
Aunque AsyncCalls puede hacer mucho más, lo he encontrado útil para este simple propósito. Supongamos que tiene una subrutina bloqueada como esta: Botón de deshabilitar, Trabajar, Activar. Mueve la parte ''Do Work'' a una función local (llámala AsyncDoWork) y añades cuatro líneas de código:
var a: IAsyncCall;
a := LocalAsyncCall(@AsyncDoWork);
while (NOT a.Finished) do
application.ProcessMessages;
a.Sync;
Lo que esto hace por usted es ejecutar AsyncDoWork en un subproceso separado, mientras que el hilo principal permanece disponible para responder a la interfaz de usuario (como arrastrar la ventana o hacer clic en Anular). Cuando termina AsyncDoWork, el código continúa. Como lo moví a una función local, todos los vars locales están disponibles, y no es necesario cambiar el código.
Este es un tipo muy limitado de ''multi-threading''. Específicamente, es doble enhebrado. Debe asegurarse de que su función Async y la IU no accedan a los mismos componentes o estructuras de datos de VCL. (Deshabilito todos los controles excepto el botón de detener).
No uso esto para escribir nuevos programas. Es solo una manera muy rápida y fácil de hacer que los programas antiguos sean más receptivos.
Para un viejo desarrollo de BDE cuando comencé con Delphi, estaba usando muchos componentes de TQuery
. Alguien me dijo que usara detalles maestros de TTable
después de que le expliqué lo que estaba haciendo, y eso hizo que el programa se ejecutara mucho más rápido.
Llamar a DisableControls
puede omitir actualizaciones innecesarias de UI.
Preasignación de listas y matrices, en lugar de aumentarlas con cada iteración.
Esto probablemente haya tenido el mayor impacto para mí en términos de velocidad.
Puede considerar el uso de paquetes de tiempo de ejecución. Esto podría reducir la huella de memoria si hay más de un programa en ejecución que está escrito utilizando los mismos paquetes.
Reduzca las operaciones de disco. Si hay suficiente memoria, cargue el archivo completamente en la RAM y realice todas las operaciones en la memoria.
Separar la lógica del programa de la interfaz de usuario, refactorizar y luego optimizar de forma independiente los elementos más utilizados y que consumen más recursos.
Si necesita utilizar Application.processmesssages (o similar) en un bucle, intente llamarlo solo cada enésima iteración.
Del mismo modo, si actualiza una barra de progreso, no la actualice cada iteración. En cambio, increméntelo en x unidades cada x iteraciones, o escale las actualizaciones de acuerdo con el tiempo o como un porcentaje de la longitud total de la tarea.
Si realmente, de verdad, necesitas ser liviano, entonces puedes descartar el VCL. Eche un vistazo a KOL & MCK . Concedido si haces eso, entonces estás comercializando funciones para reducir el espacio ocupado.
Si tiene una lista, use una matriz dinámica de cualquier cosa, incluso un registro de la siguiente manera:
Esto no necesita clases, no hay liberación y el acceso es muy rápido. Incluso si necesita crecer, puede hacer esto, ver a continuación. Solo use TList
o TStringList
si necesita mucha flexibilidad de cambio de tamaño.
type
TMyRec = record
SomeString : string;
SomeValue : double;
end;
var
Data : array of TMyRec;
I : integer;
..begin
SetLength( Data, 100 ); // defines the length and CLEARS ALL DATA
Data[32].SomeString := ''Hello'';
ShowMessage( Data[32] );
// Grow the list by 1 item.
I := Length( Data );
SetLength( Data, I+1 );
..end;
Si usa subprocesos, configure la afinidad de su procesador. Si aún no usa subprocesos, considere usarlos, o busque E / S asincrónicas (puertos de terminación) si su aplicación hace muchas E / S.
Use muchas aserciones para depurar, luego desactívelas en el código de envío.
Utilice una herramienta de perfilado Delphi (algunos here o here ) y descubra sus propios cuellos de botella. Optimizar los cuellos de botella equivocados es una pérdida de tiempo. En otras palabras, si aplica todas estas sugerencias aquí, pero ignora el hecho de que alguien puso un sueño (1000) (o similar) en algún código muy importante es una pérdida de tiempo. Repare sus cuellos de botella reales primero.
Verifique los bucles muy utilizados para los cálculos que podrían (al menos parcialmente) precalcularse o manejarse con una tabla de búsqueda. Las funciones de Trig son un clásico para esto, pero se aplica a muchas otras.
Use el FastMM completo y estudie la documentación y la fuente, y vea si puede modificarla según sus especificaciones.
- BeginUpdate ... EndUpdate
- ShortString vs. String
- Usa matrices en lugar de TStrings y TList
Pero la triste respuesta es que el ajuste y la optimización le darán tal vez un 10% de mejora (y es peligroso); el rediseño puede darle un 90%. Una vez que realmente entiende el objetivo, a menudo puede replantear el problema (y, por lo tanto, la solución) en términos mucho mejores.
Aclamaciones
Desactivar la depuración
Activar optimizaciones
Elimine todas las referencias a unidades que realmente no usa
Busque fugas de memoria