C# Arithmetic Expression Optimization

Performance optimization

In this optimization tip, you want to see how you can improve the performance of an arithmetic expression. Often, code will compute arithmetic results using a series of local variables. However, using a compound expression and storing it in a single local variable is typically faster.

This C# optimization article tests the performance of arithmetic expressions.

Optimize arithmetic

Note

First, we see a benchmarking program that tests two versions of a method that performs a load, multiply, load, multiply, addition, subtraction, and another multiply operation. The first version, Method1, uses five local variables to store the intermediate results of this computation. The second version, Method2, combines the arithmetic into a single expression. The results of the program show that Method2 is somewhat faster.

Program that measures performance [C#]

using System;
using System.Diagnostics;

class Program
{
    static int _temp1 = 5;
    static int _temp2 = 10;

    static int Method1()
    {
	// Use local variables to perform computation.
	int a = _temp1;
	int b = a * 10;
	int c = b * _temp2;
	int d = c + 5;
	int e = d - _temp2;
	return e * 2;
    }

    static int Method2()
    {
	// Use expression to perform computation.
	return ((_temp1 * 10 * _temp2) + 5 - _temp2) * 2;
    }

    const int _max = 300000000;
    static void Main()
    {
	// Test.
	Console.WriteLine(Method1());
	Console.WriteLine(Method2());
	// Clean up environment.
	GC.Collect();
	System.Threading.Thread.Sleep(10);

	for (int a = 0; a < 10; a++)
	{
	    var s1 = Stopwatch.StartNew();
	    for (int i = 0; i < _max; i++)
	    {
		Method1();
	    }
	    s1.Stop();
	    var s2 = Stopwatch.StartNew();
	    for (int i = 0; i < _max; i++)
	    {
		Method2();
	    }
	    s2.Stop();
	    Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000 * 1000) /
		_max).ToString("0.00") + " ns");
	    Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000 * 1000) /
		_max).ToString("0.00") + " ns");
	}
	Console.Read();
    }
}

Output

990
990
2.32 ns
1.92 ns
2.53 ns
2.10 ns
2.40 ns
1.91 ns
2.31 ns
1.91 ns
2.31 ns
1.91 ns
2.31 ns
1.92 ns
2.32 ns
1.92 ns
2.31 ns
1.91 ns
2.32 ns
1.91 ns
2.36 ns
1.91 ns
The C# programming language

How they are compiled. As the C# language is a high-level language, it is compiled to an intermediate form. The intermediate language in Method1 uses several local variables, which must be allocated on the activation records during execution. Method2, however, uses the evaluation stack more directly: it does not allocate local variable memory and store values in those slots.

Dragon Book: Compilers Compiler ExplanationJust-in-time compiler (JIT)

Careful. This optimization is somewhat difficult to elicit from a benchmark program. This is because the JIT (just-in-time) compiler can solve most of the inefficiencies on its own. It seems likely that some form of flow analysis or even peephole optimization could achieve this effect. Please retest this on different versions of the .NET Framework if you are in doubt.

Summary

In this tip, we looked at how you can rewrite arithmetic operations to require less execution time. To do this, we rewrote a series of local variable assignments into a single expression, which resulted in more efficient lower-level representations.

Number Examples
.NET