2012-04-11 10 views
8

Quiero obtener el máximo recuento tengo que ejecutar un bucle para que tome x milisegundos para terminar.medir la velocidad de código en .NET en milisegundos

Por ej.

int GetIterationsForExecutionTime(int ms) 
{ 
    int count = 0; 
    /* pseudocode 
    do 
     some code here 
     count++; 
    until executionTime > ms 
    */ 

    return count; 
} 

¿Cómo consigo algo como esto?

+0

'DateTime.Now' tiene una resolución de milisegundos (aunque algo inprecise), puede obtener los tiempos difíciles por' (DateTime.Now - startTime) .TotalMilliseconds'. ¿Qué tan preciso necesitas ser? – mellamokb

+0

Desviación de alrededor de -100/+ 100 ms. –

+10

'DateTime.Now' tiene una precisión de aproximadamente 1/64th de segundo en la mayoría de los sistemas operativos, FYI, lo que significa que la * diferencia * de dos fechas suele ser de hasta 1/32 de segundo, y puede sé mucho más. ** Por lo general, es mejor usar el cronómetro para este propósito. ** –

Respuesta

28

que quieren obtener el máximo recuento tengo que ejecutar un bucle para que tome x milisegundos para terminar.

En primer lugar, simplemente no hagas eso. Si necesita esperar un cierto número de milisegundos no ocupado, espere en un bucle. Más bien, inicia un temporizador y lo devuelve. Cuando el temporizador marque, haga que llame a un método que se reanude donde lo dejó. El método Task.Delay puede ser bueno para usar; se ocupa de los detalles del temporizador por ti.

Si su pregunta es en realidad acerca de cómo cronometrar la cantidad de tiempo que tarda un código, entonces necesita mucho más que simplemente un buen temporizador. Hay mucho arte y ciencia para obtener tiempos precisos.

Primero siempre debe usar Stopwatch y nunca use DateTime.Now para estos tiempos. El cronómetro está diseñado para ser un temporizador de alta precisión que le indica cuánto tiempo transcurrió. DateTime.Now es un temporizador de baja precisión para avisarle si es hora de ver Doctor Who aún. No usarías un reloj de pared para cronometrar una carrera olímpica; usarías el cronómetro de mayor precisión que pudieras tener en tus manos. Entonces usa el que está provisto para ti.

En segundo lugar, debe recordar que el código C# se compila justo a tiempo. La primera vez que pasa por un bucle puede ser cientos o miles de veces más caro que en cualquier momento posterior debido al costo de la fluctuación de fase que analiza el código que llama el bucle. Si tiene la intención de medir el costo "cálido" de un bucle, entonces necesita ejecutar el bucle una vez antes de empiece a temporizarlo. Si tiene la intención de medir el promedio costo incluyendo el tiempo de jit, entonces necesita decidir cuántas veces constituye un número razonable de ensayos, para que el promedio funcione correctamente.

En tercer lugar, necesita asegúrese de no llevar ningún peso de plomo cuando está ejecutando. Nunca realice mediciones de rendimiento mientras depura. Es asombroso el número de personas que hacen esto. Si está en el depurador, el tiempo de ejecución puede ser hablando con el depurador para asegurarse de que está obteniendo la experiencia de depuración que desea, y esa charla lleva tiempo. La inestabilidad está generando peor código que lo normal, por lo que su experiencia de depuración es más consistente. El recolector de basura es que recoge menos agresivamente. Y así. Ejecute siempre las mediciones de rendimiento fuera del depurador y con las optimizaciones activadas.

En cuarto lugar, recuerde que sistemas de memoria virtual imponen costos similares a los de jitters. Si ya está ejecutando un programa administrado, o lo ha ejecutado recientemente, entonces las páginas del CLR que necesita probablemente estén "activas", ya en la RAM, donde son rápidas. De lo contrario, las páginas podrían estar frías, en el disco, y es necesario que aparezcan errores en la página. Eso puede cambiar enormemente los tiempos.

En quinto lugar, recuerde que el jitter puede hacer optimizaciones que no espera. Si intenta tiempo:

// Let's time addition! 
for (int i = 0; i < 1000000; ++i) { int j = i + 1; } 

la fluctuación es totalmente en su derecho de eliminar todo el bucle. Puede darse cuenta de que el ciclo no computa ningún valor que se use en ningún otro lugar del programa y lo elimina por completo, lo que le da un tiempo de cero. ¿Lo hace? Tal vez. Tal vez no. Eso depende de la inestabilidad. Debe medir el rendimiento del código realista , donde los valores calculados realmente se utilizan de alguna manera; el jitter sabrá que no puede optimizarlos.

En sexto lugar, el recolector de basura puede rechazar los tiempos de las pruebas que crean mucha basura. Supongamos que tiene dos pruebas, una que hace mucha basura y otra que hace un poco. El costo de la recolección de la basura producida por la primera prueba puede "cargarse" al tiempo necesario para ejecutar la segunda prueba si, por suerte, la primera prueba se ejecuta sin una colección, pero la segunda prueba desencadena una. Si sus pruebas producen mucha basura, considere (1) que mi prueba es realista para empezar. No tiene sentido hacer una medición del rendimiento de un programa poco realista porque no puede hacer buenas inferencias sobre cómo se comportará su programa real. Y (2) ¿debería estar cargando el costo de la recolección de basura a la prueba que produjo la basura? Si es así, asegúrese de forzar una colección completa antes de que se complete el tiempo de la prueba.

Séptimo, está ejecutando su código en un entorno multiprocesador multiproceso, donde los hilos se pueden cambiar a voluntad, y donde el hilo del hilo (la cantidad de tiempo que el sistema operativo dará otro hilo hasta que el suyo tenga la oportunidad de ejecutar de nuevo) es de aproximadamente 16 milisegundos. 16 milisegundos es aproximadamente cincuenta millones de ciclos de procesador. Proceder con tiempos precisos de operaciones de menos de milisegundos puede ser bastante difícil si el cambio de hilo ocurre en uno de los varios millones de ciclos de procesador que está tratando de medir. Toma eso en consideración.

+1

En cuanto al punto tercero: He estado acostumbrando a pegar un 'if (Debugger.IsAttached) {Console.WriteLine (" El depurador está adjunto, la ejecución de referencia no es válida (¡tonto!) "; } 'en mis programas de benchmarking. – ligos

6

También puede utilizar la clase Stopwatch:

int GetIterationsForExecutionTime(int ms) 
{ 
    int count = 0; 
    Stopwatch stopwatch = new Stopwatch(); 
    stopwatch.Start();   
    do 
    { 
     // some code here 
     count++; 
    } while (stopwatch.ElapsedMilliseconds < ms); 

    stopwatch.Stop(); 
    return count; 
} 
9
var sw = new Stopwatch(); 
sw.Start(); 
... 
long elapsedMilliseconds = sw.ElapsedMilliseconds; 
+0

¿No tiene que detener el sw? :) –

+3

No, puedes leer esa propiedad en cualquier momento. – djdanlib

-1

Puntos buenos de Eric Lippert. He estado haciendo benchmarking y pruebas unitarias por un tiempo y le aconsejo que descarte cada primer paso en su código porque causa compilación de JIT. Así que en un código de evaluación comparativa de las cuales utilizan bucle y Cronómetro recordar a poner esto en el final del bucle:

   // JIT optimization. 
       if (i == 0) 
       { 
        // Discard every result you've collected. 
        // And restart the timer. 
        stopwatch.Restart(); 
       } 
Cuestiones relacionadas