c# - unitarias - pruebas de stress software ejemplos
MediciĆ³n de tiempo exacto para pruebas de rendimiento (8)
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 tardó en ingresar algo, por ejemplo, una llamada a un método?
Lo más fácil y rápido que supongo es esto:
DateTime start = DateTime.Now;
{
// Do some work
}
TimeSpan timeItTook = DateTime.Now - start;
¿Pero qué tan exacto es esto? ¿Hay mejores maneras?
Como han dicho otros, Stopwatch
debería ser la herramienta adecuada para esto. Sin embargo, se pueden realizar algunas mejoras, vea este hilo específicamente: comparando muestras de código pequeño en C #, ¿se puede mejorar esta implementación? .
He visto algunos consejos útiles de Thomas Maierhofer aquí
Básicamente su código se ve así:
//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 el tiempo que la CPU ha estado ocupada ejecutando el 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 de ayuda para realizar ambas cosas 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 del Clock
es la parte difícil. Si desea mostrar el tiempo final, depende de usted elegir qué tipo de tiempo desea. Escribí un método de extensión NormalizedMean
que le da la media de los tiempos de lectura para descartar el ruido. Quiero decir que calculo la desviación de cada tiempo de la media real, y luego descarto los valores que estaban más lejos (solo los más lentos) de la media de la desviación (llamada desviación absoluta; tenga en cuenta que no es la desviación estándar que se escucha a menudo) , y finalmente devuelve 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);
}
Como han dicho otros, el Stopwatch
es una buena clase para usar aquí. Puedes 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 a Start()
en términos de simplicidad.) Obviamente esto incurre en el éxito de invocar a un delegado, pero en la gran mayoría de los casos que ganaron " ser relevante Luego escribirías:
TimeSpan time = StopwatchUtil.Time(() =>
{
// Do some work
});
Incluso podría crear una interfaz de ITimer
para esto, con implementaciones de StopwatchTimer,
CpuTimer
StopwatchTimer,
etc., donde esté disponible.
El cronómetro está bien, pero repita el trabajo 10 ^ 6 veces, luego divídalo por 10 ^ 6. Obtendrá mucha más precisión.
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 de tiempo para pruebas de rendimiento (no en inglés)
Sí, hay alguna función 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;
}
}
System.Diagnostics.Stopwatch está diseñado para esta tarea.
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);
Usa la clase Stopwatch