Wondering what's the difference in performance for different method types in C#? Is performance the same for static, dynamic, virtual methods? What about asynchronous calls?
Here are the raw performance measurements:
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
Static | 0.0093 ns | 0.2315 ns | 0.0127 ns | 0.0042 ns | 0.0000 ns | 0.0238 ns | - | - | - | - |
StaticAsync | 19.6042 ns | 0.8500 ns | 0.0466 ns | 19.6287 ns | 19.5505 ns | 19.6334 ns | 0.0172 | - | - | 72 B |
Dynamic | 0.0601 ns | 1.2112 ns | 0.0664 ns | 0.0488 ns | 0.0000 ns | 0.1313 ns | - | - | - | - |
DynamicAsync | 20.8642 ns | 6.1827 ns | 0.3389 ns | 20.8004 ns | 20.5618 ns | 21.2305 ns | 0.0172 | - | - | 72 B |
Virtual | 0.6320 ns | 1.0647 ns | 0.0584 ns | 0.6246 ns | 0.5776 ns | 0.6937 ns | - | - | - | - |
VirtualAsync | 21.3301 ns | 6.2675 ns | 0.3435 ns | 21.4575 ns | 20.9410 ns | 21.5916 ns | 0.0172 | - | - | 72 B |
That's eye opening to be honest.
Summary
- Static methods are 6 times faster than normal instance methods.
- Static methods are 68 times faster than virtual methods.
- Virtual methods are 10.5 times slower than instance methods. Makes you think to carefully choose which methods should be virtual.
- Async calls allocate 72 bytes of memory, regardless of method signature. Normal methods have no impact on memory allocations.
- Regardless of method signature, all of the async method calls are really slow.
- The slowest method signature (virtual async) is 2293 (two thousand two hundred ninety three) times slower than the fastest method signature (static method).
Appendix. Performance Testing Source Code.
#LINQPad optimize+
void Main()
{
Util.AutoScrollResults = true;
BenchmarkRunner.Run<MethodCalls>();
}
[ShortRunJob]
[MinColumn, MaxColumn, MeanColumn, MedianColumn]
[MemoryDiagnoser]
[MarkdownExporter]
public class MethodCalls
{
private MethodsContainer _c = new MethodsContainer();
[Benchmark]
public int Static()
{
return MethodsContainer.StaticMethod(1);
}
[Benchmark]
public async Task<int> StaticAsync()
{
return await MethodsContainer.StaticAsyncMethod(1);
}
[Benchmark]
public int Dynamic()
{
return _c.DynamicMethod(1);
}
[Benchmark]
public async Task<int> DynamicAsync()
{
return await _c.DynamicAsyncMethod(1);
}
[Benchmark]
public int Virtual()
{
return _c.VirtualMethod(1);
}
[Benchmark]
public async Task<int> VirtualAsync()
{
return await _c.VirtualAsyncMethod(1);
}
}
public class MethodsContainer
{
public static int StaticMethod(int arg) { return arg * 2; }
public static Task<int> StaticAsyncMethod(int arg) { return Task.FromResult<int>(arg * 2); }
public int DynamicMethod(int arg) { return arg * 2; }
public Task<int> DynamicAsyncMethod(int arg) { return Task.FromResult<int>(arg * 2); }
public virtual int VirtualMethod(int arg) { return arg * 2; }
public virtual Task<int> VirtualAsyncMethod(int arg) { return Task.FromResult<int>(arg * 2); }
}
P.S. Originally posted on my blog - you might want to subscribe for future updates.
Top comments (3)
Hi. I'm a bit confused by your 'Dynamic' measurement. To make a call dynamic, the instance it is invoked on must be declared as dynamic, as in:
As written, your call to DynamicMethod(1) is just an instance method call.
Thank you for posting this. I also read your analysis of array iteration techniques in C#. I found them both so good that I signed up for this site specifically to thank you.
I hope you'll do more like these. Of course, I'm new here, so maybe you already have and I just haven't found them yet. Keep up the great work anyway!
Does 0.0093 ns per call mean that if it takes 1 cpu instruction you have 100GHz CPU?