performance - para - trabajo de reaccion y velocidad
Calculando la velocidad de las rutinas? (8)
Lo pregunto porque actualmente estoy tratando de optimizar algunas funciones
Es natural pensar que la medición es la forma de descubrir qué optimizar, pero hay una mejor manera.
Si algo toma una fracción de tiempo suficientemente grande (F) para que valga la pena optimizarlo, entonces si simplemente lo pausa al azar, F es la probabilidad de que lo atrape en el acto. Haga eso varias veces, y verá exactamente por qué lo hace, hasta las líneas exactas de código.
Más sobre eso. Aquí hay un ejemplo.
Arréglalo y luego realiza una medición general para ver cuánto ahorraste, lo cual debería ser aproximadamente F. Enjuagar y repetir.
¿Cuál sería la mejor y más precisa forma de determinar cuánto tiempo tomó procesar una rutina, como un procedimiento de función?
Pregunto porque actualmente estoy tratando de optimizar algunas funciones en mi aplicación, cuando pruebo los cambios es difícil de determinar con solo mirar si hubo alguna mejora. Entonces, si pudiera devolver una hora exacta o casi exacta para procesar una rutina, entonces tendré una idea más clara de qué tan bien, si se han realizado cambios en el código.
Consideré usar GetTickCount, pero no estoy seguro de si esto sería casi exacto.
Sería útil contar con una función / procedimiento de cálculo para calcular el tiempo de una rutina y usarlo de la siguiente manera:
// < prepare for calcuation of code
...
ExecuteSomeCode; // < code to test
...
// < stop calcuating code and return time it took to process
Espero escuchar algunas sugerencias.
Gracias.
Craig.
Aquí hay algunos procedimientos que hice para controlar la duración de una función. Los metí en una unidad que llamé uTesting
y luego simplemente uTesting
la cláusula uses durante mi prueba.
Declaración
Procedure TST_StartTiming(Index : Integer = 1);
//Starts the timer by storing now in Time
//Index is the index of the timer to use. 100 are available
Procedure TST_StopTiming(Index : Integer = 1;Display : Boolean = True; DisplaySM : Boolean = False);
//Stops the timer and stores the difference between time and now into time
//Displays the result if Display is true
//Index is the index of the timer to use. 100 are available
Procedure TST_ShowTime(Index : Integer = 1;Detail : Boolean = True; DisplaySM : Boolean = False);
//In a ShowMessage displays time
//Uses DateTimeToStr if Detail is false else it breaks it down (H,M,S,MS)
//Index is the index of the timer to use. 100 are available
variables declaradas
var
Time : array[1..100] of TDateTime;
Implementación
Procedure TST_StartTiming(Index : Integer = 1);
begin
Time[Index] := Now;
end;
Procedure TST_StopTiming(Index : Integer = 1;Display : Boolean = True; DisplaySM : Boolean = False);
begin
Time[Index] := Now - Time[Index];
if Display then TST_ShowTime;
end;
Procedure TST_ShowTime(Index : Integer = 1;Detail : Boolean = True; DisplaySM : Boolean = False);
var
H,M,S,MS : Word;
begin
if Detail then
begin
DecodeTime(Time[Index],H,M,S,MS);
if DisplaySM then
ShowMessage(''Hour = '' + FloatToStr(H) + #13#10 +
''Min = '' + FloatToStr(M) + #13#10 +
''Sec = '' + FloatToStr(S) + #13#10 +
''MS = '' + FloatToStr(MS) + #13#10)
else
OutputDebugString(PChar(''Hour = '' + FloatToStr(H) + #13#10 +
''Min = '' + FloatToStr(M) + #13#10 +
''Sec = '' + FloatToStr(S) + #13#10 +
''MS = '' + FloatToStr(MS) + #13#10));
end
else
ShowMessage(TimeToStr(Time[Index]));
OutputDebugString(Pchar(TimeToStr(Time[Index])));
end;
Desde Delphi 6 en adelante puede usar el contador x86 Timestamp.
Esto cuenta ciclos de CPU, en un procesador de 1 Ghz, cada cuenta demora un nanosegundo.
No se puede obtener más exacto que eso.
function RDTSC: Int64; assembler;
asm
// RDTSC can be executed out of order, so the pipeline needs to be flushed
// to prevent RDTSC from executing before your code is finished.
// Flush the pipeline
XOR eax, eax
PUSH EBX
CPUID
POP EBX
RDTSC //Get the CPU''s time stamp counter.
end;
En x64, el siguiente código es más preciso, ya que no sufre el retraso de CPUID
.
rdtscp // On x64 we can use the serializing version of RDTSC
push rbx // Serialize the code after, to avoid OoO sneaking in
push rax // subsequent instructions prior to executing RDTSCP.
push rdx // See: http://www.intel.de/content/dam/www/public/us/en/documents/white-papers/ia-32-ia-64-benchmark-code-execution-paper.pdf
xor eax,eax
cpuid
pop rdx
pop rax
pop rbx
shl rdx,32
or rax,rdx
Use el código anterior para obtener la marca de tiempo antes y después de ejecutar su código.
El método más preciso posible y fácil como circular.
Tenga en cuenta que debe ejecutar una prueba al menos 10 veces para obtener un buen resultado, en el primer paso la memoria caché estará fría y las lecturas aleatorias de disco duro y las interrupciones pueden afectar sus tiempos.
Debido a que esto es tan preciso, puede darte una idea equivocada si solo cronometras la primera carrera.
Por qué no deberías usar QueryPerformanceCounter ()
QueryPerformanceCounter()
da la misma cantidad de tiempo si la CPU se ralentiza, compensa el colapso de la CPU. Mientras RDTSC le dará la misma cantidad de ciclos si su CPU se desacelera debido al sobrecalentamiento o lo que sea.
Entonces, si su CPU comienza a funcionar en caliente y necesita acelerarse, QueryPerformanceCounter()
dirá que su rutina lleva más tiempo (lo cual es engañoso) y RDTSC dirá que lleva la misma cantidad de ciclos (lo cual es preciso) .
Esto es lo que quiere porque le interesa la cantidad de ciclos de CPU que utiliza su código, no la hora del reloj de pared.
De los últimos documentos de intel: http://software.intel.com/en-us/articles/measure-code-sections-using-the-enhanced-timer/?wapkw=%28rdtsc%29
Usando los relojes del procesador
Este temporizador es muy preciso. En un sistema con un procesador 3GHz, este temporizador puede medir eventos que duran menos de un nanosegundo. [...] Si la frecuencia cambia mientras el código objetivo se está ejecutando, la lectura final será redundante ya que las lecturas inicial y final no se tomaron usando la misma frecuencia de reloj. La cantidad de marcas de reloj que ocurrieron durante este tiempo será precisa , pero el tiempo transcurrido será desconocido.
Cuándo no usar RDTSC
RDTSC es útil para el tiempo básico. Si está sincronizando el código multiproceso en una sola máquina de CPU, RDTSC funcionará bien. Si tiene varias CPU, la cuenta de inicio puede provenir de una CPU y la cuenta de finalización de otra.
Por lo tanto, no use RDTSC para sincronizar el código multiproceso en una máquina con varias CPU. En una sola máquina de la CPU funciona bien, o código de un solo hilo en una máquina con varias CPU, también está bien.
Recuerde también que RDTSC cuenta los ciclos de CPU. Si hay algo que lleva tiempo pero no utiliza la CPU, como disk-IO o network que RDTSC no es una buena herramienta.
Pero la documentación dice que RDTSC no es precisa en las CPU modernas
RDTSC no es una herramienta para hacer un seguimiento del tiempo, es una herramienta para realizar un seguimiento de los ciclos de CPU.
Para eso, es la única herramienta que es precisa. Las rutinas que hacen un seguimiento del tiempo no son precisas en las CPU modernas porque el reloj de la CPU no es tan absoluto como solía ser.
No especificó su versión de Delphi, pero Delphi XE tiene un TStopWatch declarado en Diagnósticos de la unidad. Esto le permitirá medir el tiempo de ejecución con una precisión razonable.
uses
Diagnostics;
var
sw: TStopWatch;
begin
sw := TStopWatch.StartNew;
<dosomething>
Writeln(Format(''runtime: %d ms'', [sw.ElapsedMilliseconds]));
end;
Por lo que QueryPerformanceFrequency , el método más preciso es usar QueryPerformanceFrequency :
código:
var
Freq, StartCount, StopCount: Int64;
TimingSeconds: real;
begin
QueryPerformanceFrequency(Freq);
QueryPerformanceCounter(StartCount);
// Execute process that you want to time: ...
QueryPerformanceCounter(StopCount);
TimingSeconds := (StopCount - StartCount) / Freq;
// Display timing: ...
end;
Pruebe el Sampling Profiler de Eric Grange.
Utilice este delphi.about.com/od/windowsshellapi/a/…
clock_gettime()
es la solución alta, que es precisa para nano segundos, también puedes usar rtdsc
, que es preciso para el ciclo de la CPU, y por último puedes simplemente usar gettimeofday()
.