C# Inline Optimization

Dot Net Perls
Performance optimization

You want to see how manually inlining a method's body into an enclosing method can affect execution performance in a positive way in the C# language. While C# does not provide macros or the inline keyword, you can always paste in nested function bodies into the enclosing methods, and this influences performance.

Test method call overhead

.NET Framework information

In this benchmark program, we test the execution engine performance of the .NET Framework on two different methods: A and B. Method A contains all the necessary operations in its body directly, while method B contains another method call (C) that does part of the execution logic. The Main entry point then demonstrates the performance difference between the two first methods, showing that manually inlining improves performance by about 1.5 nanoseconds per invocation on the test computer.

Program that benchmarks inlined method [C#]

using System;
using System.Diagnostics;

class Program
{
    static int A(int v)
    {
	// This is a single method call.
	// ... It contains twenty increments.
	v++; v++; v++; v++; v++; v++; v++; v++; v++; v++;
	v++; v++; v++; v++; v++; v++; v++; v++; v++; v++;
	return v;
    }

    static int B(int v)
    {
	// This does ten increments.
	// ... Then it does ten more increments in another method.
	v++; v++; v++; v++; v++; v++; v++; v++; v++; v++;
	v = C(v);
	return v;
    }

    static int C(int v)
    {
	// This does ten increments.
	v++; v++; v++; v++; v++; v++; v++; v++; v++; v++;
	return v;
    }

    static void Main()
    {
	const int max = 100000000;
	int temp1 = 0;
	int temp2 = 0;
	A(0);
	B(0);
	C(0);

	var s1 = Stopwatch.StartNew();
	for (int i = 0; i < max; i++)
	{
	    temp1 = A(i);
	}
	s1.Stop();
	var s2 = Stopwatch.StartNew();
	for (int i = 0; i < max; i++)
	{
	    temp2 = B(i);
	}
	s2.Stop();
	Console.WriteLine(s1.Elapsed.TotalMilliseconds * 1000 * 1000 / (double)max);
	Console.WriteLine(s2.Elapsed.TotalMilliseconds * 1000 * 1000 / (double)max);
	Console.WriteLine("{0} {1}", temp1, temp2);
	Console.Read();
    }
}

Output

2.236963            (inlined)
3.820417            (regular method)
100000019 100000019

Benchmark results. The benchmark demonstrates that the first method A was measurably faster than method B, and this is due to the manually inlined method C. The benchmark also demonstrates that the result of the calls is equivalent in both loops, proving correctness.

Instructions and routines

In structural programming, developers use routines to organize complex programs. However, at their very core, computers execute instructions, not routines; compilers translate method invocations into series of instructions that operate on the evaluation stack. Unfortunately, these method instructions are pure overhead, at least when they are not required for modularity of design. This explains on a conceptual level the benefit to inlining methods. The just-in-time compiler also inlines certain methods, but typically only inlines very small methods.

Code Complete

Code Complete

Here, we mention that the book Code Complete by Steve McConnell discusses the inline routine optimization. It states that in some languages, such as C++ and C, you can use the inline keyword or macros with #define. The benchmark in that book shows that inlining a routine in C++ was mildly beneficial, but not beneficial in another language. This optimization makes some programs slower and other programs faster. You must test it to be certain of its effect.

Code Complete (Book Review)

Summary

The C# programming language

We explored inlining methods using a manual optimization technique in the C# language. While this technique can explode the complexity of your program, it can also improve performance in a measurable way. Methods are notational conveniences for programmers; processors use instructions, and compilers sometimes translate method calls into unnecessary instructions.

Method Tips