Friday, August 31, 2012

Run time costs of common operations in C#

Disclaimers:

1) The benchmarking methodology below should not be used for very small things like i++. For measuring small things see this.

2) Since writing the below I've modified measure it  for integration with linqpad - use that instead.

3) If you still want to read this, be my guest :)





Today we debated if Enum.ToString() is too slow to be used. To my surprise it takes 1-5 micro seconds on my core-i7 laptop. If 5 micro seconds is too slow for you, the internet has you covered.



Naturally, I was curious about  cost of other operations so I measured those as well. You can reproduce and extend  my experiment by putting the below code into linqpad. The output from the current code on my laptop is:



Milliseconds to perform 100000 iterations
Enum.ToString() took 411 ms
NewGuid() took 31
ms
InterlockedIncrement() took 3 ms
Lock() took 3 ms
i++ took 2 ms



static TimeSpan TimeIt(int iterations, string title, Action action)
{
var watch = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
action();
}
return watch.Elapsed;
}

struct timeable
{
public string title;
public Action code;
}

internal enum AnEnum
{
Fast,
Slow
}

void Main()
{
int interlocked=0;
object lockable = new Object();
var timeables = new timeable []
{
new timeable {title="Enum.ToString()", code = ()=>AnEnum.Fast.ToString()},
new timeable {title="NewGuid()", code = ()=>Guid.NewGuid()},
new timeable {title="Lock()", code = ()=>Monitor.Enter(lockable)},
new timeable {title="InterlockedIncrement()", code = ()=>Interlocked.Add(ref interlocked,interlocked)},
new timeable {title="i++", code = ()=>interlocked++},
};

int iterations=100*1000;
Console.WriteLine("Milliseconds to perform {0} iterations", iterations);
var timings = timeables.Select(x=>new {x.title, elapsed = TimeIt(iterations,x.title,x.code)}).OrderByDescending(x=>x.elapsed).ToList();
timings.ForEach(t=>Console.WriteLine("{0} took {1} ms",t.title,t.elapsed.TotalMilliseconds));
}