Pregunta

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

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

DateTime startTime = DateTime.Now;

// Some execution process

DateTime endTime = DateTime.Now;
TimeSpan totalTimeTaken = endTime.Subtract(startTime);
¿Fue útil?

Solución

No, no es.Utilizar el Cronógrafo (en System.Diagnostics)

Stopwatch sw = Stopwatch.StartNew();
PerformWork();
sw.Stop();

Console.WriteLine("Time taken: {0}ms", sw.Elapsed.TotalMilliseconds);

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

Vale la pena mencionar que DateTime.Now a menudo es un poco más lento que DateTime.UtcNow debido al trabajo que hay que hacer con las zonas horarias, horario de verano y tal.

DateTime.UtcNow suele tener una resolución de 15 ms.Ver Publicación del blog de John Chapman acerca de DateTime.Now Precisión para un gran resumen.

Curiosidades interesantes:El cronómetro retrocede DateTime.UtcNow si su hardware no admite un contador de alta frecuencia.Puede comprobar si el cronómetro utiliza hardware para lograr una alta precisión observando el campo estático. Cronómetro.IsHighResolution.

Otros consejos

Si quieres algo rápido y sucio, te sugiero que utilices el cronómetro para obtener 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 el uso de un generador de perfiles de terceros como HORMIGAS.

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

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

Sin embargo, resulta que el contador casi siempre existe; consulte ¿Explicación sobre el contador de rendimiento de alta resolución y su existencia relacionada con .NET Stopwatch?.

A continuación se muestra un gráfico de rendimiento.Observe el bajo costo de rendimiento que tiene UtcNow en comparación con las alternativas:

Enter image description here

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

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

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

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

Código de llamada de muestra

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 código de llamada de muestra

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

El cronógrafo la funcionalidad sería mejor (mayor precisión).Sin embargo, también recomendaría descargar uno de los generadores de perfiles populares (Trazo de puntos y HORMIGAS son los que más he usado...la prueba gratuita de DotTrace es completamente funcional y no molesta como algunas otras).

Utilice 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.

Lo mismo ocurre con el cronómetro, es mucho mejor.

Con respecto a la medición del desempeño, 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 "// Algún proceso de ejecución" puede ser mucho más lenta que las ejecuciones posteriores.

Normalmente pruebo un método ejecutándolo 1000 veces o 1000000 de veces en un bucle y obtengo datos mucho más precisos que ejecutándolo una vez.

Todas estas son excelentes formas de medir el tiempo, pero es sólo una forma muy indirecta de encontrar cuellos de botella.

La forma más directa de encontrar un cuello de botella en un hilo es ponerlo en funcionamiento y, mientras hace lo que te hace esperar, detenerlo con una tecla de pausa o interrupción.Haz esto varias veces.Si su cuello de botella ocupa el X% del tiempo, el X% es la probabilidad de que lo detecte en el acto en cada instantánea.

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

@Sean Cámaras

Para su información, la clase .NET Timer no es para diagnóstico, genera eventos en un intervalo preestablecido, como este (de 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 te ayuda a saber cuánto tiempo tomó algo, solo que ha pasado una cierta cantidad de tiempo.

El temporizador también está expuesto como un control en System.Windows.Forms...Puedes encontrarlo en tu 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 más información pasa por Utilice Stopwatch en lugar de DataTime para obtener un contador de rendimiento preciso.

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

El grupo de Patrones y Prácticas de Microsoft tiene alguna orientación sobre Guía de pruebas de rendimiento del sistema Visual Studio Team.

Acabo de encontrar una publicación en el blog de Vance Morrison sobre una clase CodeTimer él escribió que hace uso StopWatch más fácil y hace algunas cosas interesantes aparte.

He hecho muy poco de este tipo de verificación de rendimiento (tiendo a pensar simplemente "esto es lento, hazlo más rápido"), así que casi siempre he optado por esto.

Google revela muchos recursos/artículos para comprobar el rendimiento.

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

Editar:

Visto las charlas de StopWatch..¡Lindo!He aprendido algo :)

Este parece un buen articulo.

La forma en que uso dentro de mis programas 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é la llamada GC.Collect para cambiar el montón administrado a un estado conocido y agregaré la llamada Sleep para que diferentes intervalos de código se puedan separar fácilmente en el perfil ETW.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top