This project is read-only.

Project: Diagnostic.Benchmark

Benchmark class for performance tests:
void Main()
{
	List<string> strings = new List<string>() { "1", "a", "3", "b" };
	
	Func<IEnumerable<string>, IEnumerable<int>> func1 =
		list => list.Where(x => new Int32Converter().IsValid(x)).Select(x => int.Parse(x));
		
	Func<IEnumerable<string>, IEnumerable<int>> func2 =
		list => { var ret = 0; return list.Where(x => int.TryParse(x, out ret)).Select(x => ret); };
	
	Benchmark
		.For(1000)
			.Execute("Int32Converter", () => func1(strings).Iterate())
			.Execute("int.TryParse", () => func2(strings).Iterate())
		.Report(4).Dump();
}

public static class Benchmark
{
	public static BenchmarkActionScheduler For(int count)
	{
		return
			new BenchmarkActionScheduler(count);
	}
}

public class BenchmarkActionScheduler
	: IBenchmarkActionScheduler
{
	private readonly int count;
	private readonly IDictionary<string, Action> actionDictionary =
		new Dictionary<string, Action>();
	private int actionIndex;
	
	internal BenchmarkActionScheduler(int count)
	{
		this.count = count;
	}
	
	public IBenchmarkActionScheduler Execute(Action action)
	{
		return Execute((actionIndex++).ToString(), action);
	}
	
	public IBenchmarkActionScheduler Execute(string name, Action action)
	{
		actionDictionary.Add(name, action);
		return this;
	}

	IEnumerable<Report> IBenchmarkActionScheduler.Report(int? digits)
	{
		var reportList = new List<Report>();
		
		foreach (var action in actionDictionary)
		{
			var stopwatch = Stopwatch.StartNew();
			
			var localAction = action;
			
			Enumerable
				.Range(0, count)
				.Iterate(() => localAction.Value());

			reportList
				.Add(new Report(action.Key, stopwatch.ElapsedMilliseconds));
		}
		
		var maxTime = reportList.Max(x => x.Milliseconds);
		
		foreach (var report in reportList)
		{
			var percentage = (report.Milliseconds * 100) / (double)maxTime;
			
			if (digits.HasValue)
			{
				percentage = Math.Round(percentage, digits.Value);
			}
			
			report.Percentage = percentage;
			report.TimesFaster = (int)Math.Round(maxTime / (double)report.Milliseconds);
		}
		
		return reportList;		
	}
}

public interface IBenchmarkActionScheduler
{
	IEnumerable<Report> Report(int? digits = null);

	IBenchmarkActionScheduler Execute(Action action);

	IBenchmarkActionScheduler Execute(string name, Action action);
}

public class Report
{
	public Report(string operationName, long milliseconds)
	{
		OperationName = operationName;
		Milliseconds = milliseconds;
	}
	
	public string OperationName { get; private set; }
	
	public long Milliseconds { get; private set; }
	
	public double Percentage { get; internal set; }
	
	public long TimesFaster { get; internal set; }
}

public static class EnumerableExensions
{
	public static void Iterate<T>(this IEnumerable<T> collection)
	{
		var enumerator = collection.GetEnumerator();

		while (enumerator.MoveNext())
		{
		}
	}
	
	public static void Iterate<T>(this IEnumerable<T> collection, Action action)
	{
		var enumerator = collection.GetEnumerator();
		
		while (enumerator.MoveNext())
		{
			action();
		}
	}
}

Last edited May 29, 2012 at 11:06 AM by teoarch, version 3

Comments

eekstein May 26, 2013 at 7:36 AM 
This does not seem to be accurate. It seems if the two functions are near a tie then the second one always runs faster,. When I reverse the order they execute the second one is faster. Also, if I run the same method twice the second one is always faster. Am I missing something?