DEV Community

loading...

C#/.NET Method Call Performance - Facts

Ivan G
Software and data architect, independent consultant and cloud computing professional
・2 min read

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

  1. Static methods are 6 times faster than normal instance methods.
  2. Static methods are 68 times faster than virtual methods.
  3. Virtual methods are 10.5 times slower than instance methods. Makes you think to carefully choose which methods should be virtual.
  4. Async calls allocate 72 bytes of memory, regardless of method signature. Normal methods have no impact on memory allocations.
  5. Regardless of method signature, all of the async method calls are really slow.
  6. 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); }
}
Enter fullscreen mode Exit fullscreen mode

P.S. Originally posted on my blog - you might want to subscribe for future updates.

Discussion (0)