Как насчет того, чтобы расширить класс Секундомера?
public static class StopwatchExtensions
{
public static long Time(this Stopwatch sw, Action action, int iterations)
{
sw.Reset();
sw.Start();
for (int i = 0; i < iterations; i++)
{
action();
}
sw.Stop();
return sw.ElapsedMilliseconds;
}
}
Тогда вызов это как это:
var s = new Stopwatch();
Console.WriteLine(s.Time(() => DoStuff(), 1000));
Вы могли добавить другую перегрузку, которая опускает "итеративный" параметр и называет эту версию с некоторым значением по умолчанию (как 1 000).
Вы могли попытаться писать дополнительный метод для любого класса, который Вы используете (или любой базовый класс).
у меня был бы вызов, будьте похожи:
Stopwatch sw = MyObject.TimedFor(1000, () => DoStuff(s));
Тогда дополнительный метод:
public static Stopwatch TimedFor(this DependencyObject source, Int32 loops, Action action)
{
var sw = new Stopwatch();
sw.Start();
for (int i = 0; i < loops; ++i)
{
action.Invoke();
}
sw.Stop();
return sw;
}
Любой объект, происходящий из DependencyObject, может теперь назвать TimedFor (..). Функция может легко быть скорректирована для обеспечения возвращаемых значений через касательно параметрических усилителей.
-
, Если Вы не хотели, чтобы функциональность была связана с каким-либо классом / возражают, что Вы могли сделать что-то как:
public class Timing
{
public static Stopwatch TimedFor(Action action, Int32 loops)
{
var sw = new Stopwatch();
sw.Start();
for (int i = 0; i < loops; ++i)
{
action.Invoke();
}
sw.Stop();
return sw;
}
}
Тогда Вы могли использовать его как:
Stopwatch sw = Timing.TimedFor(() => DoStuff(s), 1000);
Сбой, что, этот ответ похож на него, имеет некоторую достойную "универсальную" способность:
Переносящаяся синхронизация StopWatch с делегатом или лямбдой?
Я записал простой класс CodeProfiler некоторое время назад, который обернул Секундомер для легкого профилирования метода с помощью Действия: http://www.improve.dk/blog/2008/04/16/profiling-code-the-easy-way
Это также легко позволит Вам представлять многопоточный код. Следующий пример представит лямбду действия с 1-16 потоками:
static void Main(string[] args)
{
Action action = () =>
{
for (int i = 0; i < 10000000; i++)
Math.Sqrt(i);
};
for(int i=1; i<=16; i++)
Console.WriteLine(i + " thread(s):\t" +
CodeProfiler.ProfileAction(action, 100, i));
Console.Read();
}
Для принятия Вас просто нужна быстрая синхронизация одной вещи, это просто в использовании.
public static class Test {
public static void Invoke() {
using( SingleTimer.Start )
Thread.Sleep( 200 );
Console.WriteLine( SingleTimer.Elapsed );
using( SingleTimer.Start ) {
Thread.Sleep( 300 );
}
Console.WriteLine( SingleTimer.Elapsed );
}
}
public class SingleTimer :IDisposable {
private Stopwatch stopwatch = new Stopwatch();
public static readonly SingleTimer timer = new SingleTimer();
public static SingleTimer Start {
get {
timer.stopwatch.Reset();
timer.stopwatch.Start();
return timer;
}
}
public void Stop() {
stopwatch.Stop();
}
public void Dispose() {
stopwatch.Stop();
}
public static TimeSpan Elapsed {
get { return timer.stopwatch.Elapsed; }
}
}
Можно перегрузить много методов для покрытия различных случаев параметров, которые Вы могли бы хотеть передать лямбде:
public static Stopwatch MeasureTime<T>(int iterations, Action<T> action, T param)
{
var sw = new Stopwatch();
sw.Start();
for (int i = 0; i < iterations; i++)
{
action.Invoke(param);
}
sw.Stop();
return sw;
}
public static Stopwatch MeasureTime<T, K>(int iterations, Action<T, K> action, T param1, K param2)
{
var sw = new Stopwatch();
sw.Start();
for (int i = 0; i < iterations; i++)
{
action.Invoke(param1, param2);
}
sw.Stop();
return sw;
}
, С другой стороны, можно использовать делегата Func, если они должны возвратить значение. Можно также передать в массиве (или больше) параметров, если каждое повторение должно использовать уникальное значение.
Мне нравится использовать классы CodeTimer от Vance Morrison (один из чуваков производительности от.NET).
Он сделал сообщение на на его блоге названным" Имеющий размеры управляемый код быстро и easiliy: CodeTimers".
Это включает интересный материал, такой как MultiSampleCodeTimer. Это делает автоматическое вычисление среднего и стандартного отклонения и его также очень легкий распечатать Ваши результаты.
Для меня расширение кажется немного более интуитивным в int, вам больше не нужно создавать секундомер или беспокоиться о его сбросе.
Итак, у вас есть:
static class BenchmarkExtension {
public static void Times(this int times, string description, Action action) {
Stopwatch watch = new Stopwatch();
watch.Start();
for (int i = 0; i < times; i++) {
action();
}
watch.Stop();
Console.WriteLine("{0} ... Total time: {1}ms ({2} iterations)",
description,
watch.ElapsedMilliseconds,
times);
}
}
С примером использования:
var randomStrings = Enumerable.Range(0, 10000)
.Select(_ => Guid.NewGuid().ToString())
.ToArray();
50.Times("Add 10,000 random strings to a Dictionary",
() => {
var dict = new Dictionary<string, object>();
foreach (var str in randomStrings) {
dict.Add(str, null);
}
});
50.Times("Add 10,000 random strings to a SortedList",
() => {
var list = new SortedList<string, object>();
foreach (var str in randomStrings) {
list.Add(str, null);
}
});
Пример вывода:
Add 10,000 random strings to a Dictionary ... Total time: 144ms (50 iterations)
Add 10,000 random strings to a SortedList ... Total time: 4088ms (50 iterations)
Вот что я использовал:
public class DisposableStopwatch: IDisposable {
private readonly Stopwatch sw;
private readonly Action<TimeSpan> f;
public DisposableStopwatch(Action<TimeSpan> f) {
this.f = f;
sw = Stopwatch.StartNew();
}
public void Dispose() {
sw.Stop();
f(sw.Elapsed);
}
}
Использование:
using (new DisposableStopwatch(t => Console.WriteLine("{0} elapsed", t))) {
// do stuff that I want to measure
}
The StopWatch
class does not need to be Disposed
or Stopped
on error. So, the simplest code to time some action is
public partial class With
{
public static long Benchmark(Action action)
{
var stopwatch = Stopwatch.StartNew();
action();
stopwatch.Stop();
return stopwatch.ElapsedMilliseconds;
}
}
Sample calling code
public void Execute(Action action)
{
var time = With.Benchmark(action);
log.DebugFormat(“Did action in {0} ms.”, time);
}
I don't like the idea of including the iterations into the StopWatch
code. You can always create another method or extension that handles executing N
iterations.
public partial class With
{
public static void Iterations(int n, Action action)
{
for(int count = 0; count < n; count++)
action();
}
}
Sample calling code
public void Execute(Action action, int n)
{
var time = With.Benchmark(With.Iterations(n, action));
log.DebugFormat(“Did action {0} times in {1} ms.”, n, time);
}
Here are the extension method versions
public static class Extensions
{
public static long Benchmark(this Action action)
{
return With.Benchmark(action);
}
public static Action Iterations(this Action action, int n)
{
return () => With.Iterations(n, action);
}
}
And sample calling code
public void Execute(Action action, int n)
{
var time = action.Iterations(n).Benchmark()
log.DebugFormat(“Did action {0} times in {1} ms.”, n, time);
}
I tested the static methods and extension methods (combining iterations and benchmark) and the delta of expected execution time and real execution time is <= 1 ms.