Medición del tiempo exacto para pruebas de rendimiento [duplicar]
Esta pregunta ya tiene una respuesta aquí:
- ¿Cómo medir el rendimiento del código en. NET? 18 respuestas
¿Cuál es la forma más exacta de ver cuánto tiempo algo, por ejemplo una llamada a un método, tomó en código?
Lo más fácil y rápido que podría adivinar es esto:
DateTime start = DateTime.Now;
{
// Do some work
}
TimeSpan timeItTook = DateTime.Now - start;
Pero ¿qué tan exacto es esto? Hay mejores maneras?
8 answers
Una mejor manera es usar la clase Cronómetro:
using System.Diagnostics;
// ...
Stopwatch sw = new Stopwatch();
sw.Start();
// ...
sw.Stop();
Console.WriteLine("Elapsed={0}",sw.Elapsed);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-24 11:05:00
Como otros han dicho, Stopwatch
es una buena clase para usar aquí. Puede envolverlo en un método útil:
public static TimeSpan Time(Action action)
{
Stopwatch stopwatch = Stopwatch.StartNew();
action();
stopwatch.Stop();
return stopwatch.Elapsed;
}
(Tenga en cuenta el uso de Stopwatch.StartNew()
. Prefiero esto a crear un cronómetro y luego llamar Start()
en términos de simplicidad.) Obviamente esto incurre en el golpe de invocar a un delegado, pero en la gran mayoría de los casos eso no será relevante. Entonces escribirías:
TimeSpan time = StopwatchUtil.Time(() =>
{
// Do some work
});
Incluso podría hacer una interfaz ITimer
para esto, con implementaciones de StopwatchTimer,
CpuTimer
etc donde esté disponible.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-09 10:46:38
Como otros dijeron, Stopwatch
debería ser la herramienta correcta para esto. Sin embargo, puede haber pocas mejoras, ver este hilo específicamente: Benchmarking de pequeñas muestras de código en C#, ¿se puede mejorar esta implementación?.
He visto algunos consejos útiles de Thomas Maierhofer aquí
Básicamente su código se ve como:
//prevent the JIT Compiler from optimizing Fkt calls away
long seed = Environment.TickCount;
//use the second Core/Processor for the test
Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(2);
//prevent "Normal" Processes from interrupting Threads
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
//prevent "Normal" Threads from interrupting this thread
Thread.CurrentThread.Priority = ThreadPriority.Highest;
//warm up
method();
var stopwatch = new Stopwatch()
for (int i = 0; i < repetitions; i++)
{
stopwatch.Reset();
stopwatch.Start();
for (int j = 0; j < iterations; j++)
method();
stopwatch.Stop();
print stopwatch.Elapsed.TotalMilliseconds;
}
Otro enfoque es confiar en Process.TotalProcessTime
para medir cuánto tiempo la CPU se ha mantenido ocupada ejecutando el mismo código / proceso, como se muestra aquí Esto puede reflejar un escenario más real, ya que ningún otro proceso afecta la medición. Hace algo como:
var start = Process.GetCurrentProcess().TotalProcessorTime;
method();
var stop = Process.GetCurrentProcess().TotalProcessorTime;
print (end - begin).TotalMilliseconds;
Una implementación desnuda y detallada de lo mismo se puede encontrar aquí.
Escribí una clase auxiliar para realizar ambos de una manera fácil de usar:
public class Clock
{
interface IStopwatch
{
bool IsRunning { get; }
TimeSpan Elapsed { get; }
void Start();
void Stop();
void Reset();
}
class TimeWatch : IStopwatch
{
Stopwatch stopwatch = new Stopwatch();
public TimeSpan Elapsed
{
get { return stopwatch.Elapsed; }
}
public bool IsRunning
{
get { return stopwatch.IsRunning; }
}
public TimeWatch()
{
if (!Stopwatch.IsHighResolution)
throw new NotSupportedException("Your hardware doesn't support high resolution counter");
//prevent the JIT Compiler from optimizing Fkt calls away
long seed = Environment.TickCount;
//use the second Core/Processor for the test
Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(2);
//prevent "Normal" Processes from interrupting Threads
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
//prevent "Normal" Threads from interrupting this thread
Thread.CurrentThread.Priority = ThreadPriority.Highest;
}
public void Start()
{
stopwatch.Start();
}
public void Stop()
{
stopwatch.Stop();
}
public void Reset()
{
stopwatch.Reset();
}
}
class CpuWatch : IStopwatch
{
TimeSpan startTime;
TimeSpan endTime;
bool isRunning;
public TimeSpan Elapsed
{
get
{
if (IsRunning)
throw new NotImplementedException("Getting elapsed span while watch is running is not implemented");
return endTime - startTime;
}
}
public bool IsRunning
{
get { return isRunning; }
}
public void Start()
{
startTime = Process.GetCurrentProcess().TotalProcessorTime;
isRunning = true;
}
public void Stop()
{
endTime = Process.GetCurrentProcess().TotalProcessorTime;
isRunning = false;
}
public void Reset()
{
startTime = TimeSpan.Zero;
endTime = TimeSpan.Zero;
}
}
public static void BenchmarkTime(Action action, int iterations = 10000)
{
Benchmark<TimeWatch>(action, iterations);
}
static void Benchmark<T>(Action action, int iterations) where T : IStopwatch, new()
{
//clean Garbage
GC.Collect();
//wait for the finalizer queue to empty
GC.WaitForPendingFinalizers();
//clean Garbage
GC.Collect();
//warm up
action();
var stopwatch = new T();
var timings = new double[5];
for (int i = 0; i < timings.Length; i++)
{
stopwatch.Reset();
stopwatch.Start();
for (int j = 0; j < iterations; j++)
action();
stopwatch.Stop();
timings[i] = stopwatch.Elapsed.TotalMilliseconds;
print timings[i];
}
print "normalized mean: " + timings.NormalizedMean().ToString();
}
public static void BenchmarkCpu(Action action, int iterations = 10000)
{
Benchmark<CpuWatch>(action, iterations);
}
}
Solo llama
Clock.BenchmarkTime(() =>
{
//code
}, 10000000);
O
Clock.BenchmarkCpu(() =>
{
//code
}, 10000000);
La última parte de la Clock
es la parte difícil. Si desea mostrar el el tiempo final, depende de usted elegir qué tipo de tiempo desea. Escribí un método de extensión NormalizedMean
que te da la media de los tiempos de lectura descartando el ruido. Quiero decir que calculo la desviación de cada tiempo de la media real, y luego descarto los valores que eran más lejanos (solo los más lentos) de la media de desviación (llamada desviación absoluta; tenga en cuenta que no es la desviación estándar a menudo escuchada), y finalmente devuelvo la media de los valores restantes. Esto significa, por ejemplo, si los valores cronometrados son { 1, 2, 3, 2, 100 }
(en ms o lo que sea), descarta 100
, y devuelve la media de { 1, 2, 3, 2 }
que es 2
. O si los tiempos son { 240, 220, 200, 220, 220, 270 }
, descarta 270
, y devuelve la media de { 240, 220, 200, 220, 220 }
que es 220
.
public static double NormalizedMean(this ICollection<double> values)
{
if (values.Count == 0)
return double.NaN;
var deviations = values.Deviations().ToArray();
var meanDeviation = deviations.Sum(t => Math.Abs(t.Item2)) / values.Count;
return deviations.Where(t => t.Item2 > 0 || Math.Abs(t.Item2) <= meanDeviation).Average(t => t.Item1);
}
public static IEnumerable<Tuple<double, double>> Deviations(this ICollection<double> values)
{
if (values.Count == 0)
yield break;
var avg = values.Average();
foreach (var d in values)
yield return Tuple.Create(d, avg - d);
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:55:09
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-09 10:39:10
Sistema.Diagnostico.Cronómetro está diseñado para esta tarea.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-09 10:40:44
Cronómetro está bien, pero bucle el trabajo 10^6 veces, a continuación, dividir por 10^6. Obtendrás mucha más precisión.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-09 21:34:07
Estoy usando esto:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(myUrl);
System.Diagnostics.Stopwatch timer = new Stopwatch();
timer.Start();
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
statusCode = response.StatusCode.ToString();
response.Close();
timer.Stop();
De mi blog: C # Medición Del Tiempo Para Pruebas De Rendimiento (No en Inglés)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-12 17:30:31
Sí hay algunas funciones en el kernel de Windows
[System.Runtime.InteropServices.DllImport("KERNEL32")]
private static extern bool QueryPerformanceCounter(ref long lpPerformanceCount);
[System.Runtime.InteropServices.DllImport("KERNEL32")]
private static extern bool QueryPerformanceFrequency(ref long lpFrequency);
public static float CurrentSecond
{
get
{
long current = 0;
QueryPerformanceCounter(ref current);
long frequency = 0;
QueryPerformanceFrequency(ref frequency);
return (float) current / (float) frequency;
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-17 07:52:54