¿DateTime.Now es la mejor manera de medir el rendimiento de una función?

Necesito encontrar un cuello de botella y necesito medir el tiempo con la mayor precisión posible.

¿El siguiente fragmento de código es la mejor manera de medir el rendimiento?

DateTime startTime = DateTime.Now; // Some execution process DateTime endTime = DateTime.Now; TimeSpan totalTimeTaken = endTime.Subtract(startTime); 

No, no es. Use el cronómetro (en System.Diagnostics )

 Stopwatch sw = Stopwatch.StartNew(); PerformWork(); sw.Stop(); Console.WriteLine("Time taken: {0}ms", sw.Elapsed.TotalMilliseconds); 

Cronómetro comprueba automáticamente la existencia de temporizadores de alta precisión.

Vale la pena mencionar que DateTime.Now menudo es bastante más lento que DateTime.UtcNow debido al trabajo que tiene que hacerse con las zonas horarias , horario de verano y tal.

DateTime.UtcNow normalmente tiene una resolución de 15 ms. Consulte la publicación del blog de John Chapman sobre DateTime.Now precision para obtener un excelente resumen.

Curiosidades interesantes: el cronómetro vuelve a DateTime.UtcNow en DateTime.UtcNow si su hardware no es compatible con un contador de alta frecuencia. Puede verificar si Stopwatch usa hardware para lograr alta precisión mirando el campo estático Stopwatch.IsHighResolution .

Si quiere algo rápido y sucio, le sugiero que use el cronómetro para un mayor grado de precisión.

 Stopwatch sw = new Stopwatch(); sw.Start(); // Do Work sw.Stop(); Console.WriteLine("Elapsed time: {0}", sw.Elapsed.TotalMilliseconds); 

Alternativamente, si necesita algo un poco más sofisticado, probablemente debería considerar usar un generador de perfiles de terceros como ANTS .

Este artículo dice que antes que nada debe comparar tres alternativas, Stopwatch , DateTime.Now Y DateTime.UtcNow .

También muestra que, en algunos casos (cuando el contador de rendimiento no existe), Cronómetro utiliza DateTime.UtcNow + algún procesamiento adicional. Por eso es obvio que en ese caso DateTime.UtcNow es la mejor opción (porque otros la usan + algún procesamiento)

Sin embargo, como resulta, el contador casi siempre existe – ver Explicación sobre el contador de rendimiento de alta resolución y su existencia relacionada con .NET Stopwatch? .

Aquí hay un gráfico de rendimiento. Observe cómo el bajo costo de rendimiento de UtcNow se ha comparado con las alternativas:

Ingrese la descripción de la imagen aquí

El eje X es el tamaño de datos de muestra, y el eje Y es el tiempo relativo del ejemplo.

Una cosa en la que el Stopwatch es mejor es que proporciona mediciones de tiempo de resolución más alta. Otra es su naturaleza más OO. Sin embargo, crear un contenedor OO alrededor de UtcNow no puede ser difícil.

Es útil insertar su código de evaluación comparativa en una clase / método de utilidad. La clase StopWatch no necesita ser Disposed o Stopped en caso de error. Entonces, el código más simple para cronometrar alguna acción es

 public partial class With { public static long Benchmark(Action action) { var stopwatch = Stopwatch.StartNew(); action(); stopwatch.Stop(); return stopwatch.ElapsedMilliseconds; } } 

Ejemplo de código de llamada

 public void Execute(Action action) { var time = With.Benchmark(action); log.DebugFormat(“Did action in {0} ms.”, time); } 

Aquí está la versión del método de extensión

 public static class Extensions { public static long Benchmark(this Action action) { return With.Benchmark(action); } } 

Y muestra el código de llamada

 public void Execute(Action action) { var time = action.Benchmark() log.DebugFormat(“Did action in {0} ms.”, time); } 

La funcionalidad del cronómetro sería mejor (mayor precisión). También recomendaría simplemente descargar uno de los perfiladores populares, ( DotTrace y ANTS son los que he usado más … la versión de prueba gratuita de DotTrace es completamente funcional y no molesta como algunos de los demás).

Use la clase System.Diagnostics.Stopwatch.

 Stopwatch sw = new Stopwatch(); sw.Start(); // Do some code. sw.Stop(); // sw.ElapsedMilliseconds = the time your "do some code" took. 

Ditto Stopwatch, es mucho mejor.

Con respecto a la medición del rendimiento, también debe verificar si su “// Algún Proceso de Ejecución” es un proceso muy corto.

También tenga en cuenta que la primera ejecución de su “// Some Execution Process” puede ser mucho más lenta que las ejecuciones posteriores.

Por lo general, pruebo un método ejecutándolo 1000 veces o 1000000 veces en un ciclo y obtengo datos mucho más precisos que cuando lo ejecuto una vez.

Estas son todas excelentes maneras de medir el tiempo, pero eso es solo una forma muy indirecta de encontrar cuellos de botella.

La forma más directa de encontrar un cuello de botella en un hilo es hacerlo funcionar, y mientras hace lo que te hace esperar, detente con una pausa o una tecla de corte. Haz esto varias veces. Si su cuello de botella toma X% de tiempo, X% es la probabilidad de que lo atrape en el acto en cada instantánea.

Aquí hay una explicación más completa de cómo y por qué funciona

@ Sean Chambers

FYI, la clase .NET Timer no es para diagnósticos, genera eventos en un intervalo preestablecido, como este (desde MSDN ):

 System.Timers.Timer aTimer; public static void Main() { // Create a timer with a ten second interval. aTimer = new System.Timers.Timer(10000); // Hook up the Elapsed event for the timer. aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent); // Set the Interval to 2 seconds (2000 milliseconds). aTimer.Interval = 2000; aTimer.Enabled = true; Console.WriteLine("Press the Enter key to exit the program."); Console.ReadLine(); } // Specify what you want to happen when the Elapsed event is // raised. private static void OnTimedEvent(object source, ElapsedEventArgs e) { Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime); } 

Así que esto realmente no ayuda a saber cuánto tiempo tomó algo, solo que ha pasado una cierta cantidad de tiempo.

El temporizador también está expuesto como control en System.Windows.Forms … puede encontrarlo en su caja de herramientas de diseño en VS05 / VS08

Esta es la forma correcta:

 using System; using System.Diagnostics; class Program { public static void Main() { Stopwatch stopWatch = Stopwatch.StartNew(); // some other code stopWatch.Stop(); // this not correct to get full timer resolution Console.WriteLine("{0} ms", stopWatch.ElapsedMilliseconds); // Correct way to get accurate high precision timing Console.WriteLine("{0} ms", stopWatch.Elapsed.TotalMilliseconds); } } 

Para obtener más información, utilice Usar cronómetro en lugar de DataTime para obtener un contador de rendimiento preciso .

Visual Studio Team System tiene algunas características que pueden ayudar con este problema. Básicamente, puede escribir pruebas unitarias y mezclarlas en diferentes escenarios para ejecutarlas en su software como parte de una prueba de esfuerzo o carga. Esto puede ayudar a identificar las áreas de código que más impactan en el rendimiento de sus aplicaciones.

El grupo de Patrones y Prácticas de Microsoft tiene alguna guía en la Guía de Pruebas de Desempeño del Sistema Visual Studio Team .

Acabo de encontrar una publicación en el blog de Vance Morrison sobre una clase de CodeTimer que escribió que hace que usar el StopWatch sea ​​más fácil y hace algunas cosas ordenadas en el costado.

He hecho muy poco de este tipo de comprobación de rendimiento (tiendo a pensar “esto es lento, lo hago más rápido”) así que casi siempre me he ido con esto.

Un google revela una gran cantidad de recursos / artículos para la verificación de rendimiento.

Muchos mencionan el uso de pinvoke para obtener información de rendimiento. Muchos de los materiales que estudio solo mencionan el uso del perfmon …

Editar:

Visto las conversaciones de StopWatch … ¡Agradable! He aprendido algo 🙂

Esto parece un buen artículo

La forma en que uso dentro de mis progtwigs es usando la clase StopWatch como se muestra aquí.

 Stopwatch sw = new Stopwatch(); sw.Start(); // Critical lines of code long elapsedMs = sw.Elapsed.TotalMilliseconds; 

Esto no es lo suficientemente profesional:

 Stopwatch sw = Stopwatch.StartNew(); PerformWork(); sw.Stop(); Console.WriteLine("Time taken: {0}ms", sw.Elapsed.TotalMilliseconds); 

Una versión más confiable es:

 PerformWork(); int repeat = 1000; Stopwatch sw = Stopwatch.StartNew(); for (int i = 0; i < repeat; i++) { PerformWork(); } sw.Stop(); Console.WriteLine("Time taken: {0}ms", sw.Elapsed.TotalMilliseconds / repeat); 

En mi código real, agregaré GC.Collect call para cambiar el montón administrado a un estado conocido, y agregaré Sleep call para que los diferentes intervalos de código puedan separarse fácilmente en el perfil ETW.