TheDeveloperBlog.com


C# Method Size Test

Method size influences performance. It affects the JIT compiler in the .NET Framework. Why do shorter methods that follow the same paths at runtime run faster than longer methods? We gain an understanding of how the optimizer works.

Question: Are shorter methods faster even if the same statements are executed?
The result was that shorter methods are faster.


Example. First we see the benchmark. There are two methods being tested. The first one is short and doesn't contain code that isn't executed. The second one, which follows the same steps but contains unnecessary code at the end, is never used.

C# program that tests method sizes

using System;
using System.Diagnostics;

class Program
{
    static void Main()
    {
	// [Note]
	// Shows how method size can impact performance.
	const int m = 10000000;
	Stopwatch s1 = Stopwatch.StartNew();
	for (int i = 0; i < m; i++)
	{
	    int a = Method1();
	    int b = Method1();
	    if (a != b) // [ignore]
	    {
		i++;
	    }
	}
	s1.Stop();
	Stopwatch s2 = Stopwatch.StartNew();
	for (int i = 0; i < m; i++)
	{
	    int a = Method2();
	    int b = Method2();
	    if (a != b) // [ignore]
	    {
		i++;
	    }
	}
	s2.Stop();
	Console.WriteLine("{0},{1}",
	    s1.ElapsedMilliseconds,
	    s2.ElapsedMilliseconds);
	Console.Read();
    }

    /// <summary>
    /// Short method.
    /// </summary>
    static int Method1()
    {
	int i = int.Parse("1");
	if (i == 1)
	{
	    return 1;
	}
	return 0;
    }

    /// <summary>
    /// Long method with unneeded logic that is never used.
    /// </summary>
    static int Method2()
    {
	int i = int.Parse("1");
	if (i == 1)
	{
	    return 1;
	}
	// [begin useless code]
	string a = 1.ToString();
	string b = 1.ToString();
	string c = 1.ToString();
	string d = 1.ToString();
	string e = 1.ToString();
	string f = 1.ToString();
	string g = 1.ToString();
	string h = 1.ToString();
	return (a != b ||
	    a != c ||
	    a != d ||
	    a != e ||
	    a != f ||
	    a != g ||
	    a != h) ? 1 : 0;
	// [end]
    }
}

Results

Method 1 (no extra code):         2269 ms [7.43% faster]
Method 2 (has extra unused code): 2451 ms

Main method. This method runs two loops and times them with Stopwatches. The first loop tests Method1, and then the second loop tests Method2. The elapsed milliseconds are printed afterwards.

Method1: This method always returns the value 1 and doesn't have any unreachable code at the end. It performs better.

Method2: This method returns the value 1. It has unreachable code at the end. The code after the return statement is never reached.

However: The compiler cannot remove code from Method2 because it doesn't know the result of int.Parse at compile-time.


Benchmark. My test found the first method to always be faster than the second. This means that unreachable code that is not compiled away causes a performance hit. I measured the two methods in many configurations.

The method without the extra statements performed about 7% faster. As part of my test, I checked IL Disassembler and saw that Method1 has 1 local, while Method2 has 17 locals. This affects stack memory usage.

IL Disassembler Tutorial

Summary. Code not executed affects the performance of your methods. You can mitigate the performance loss when you need to have long groups of statements that are not executed often by refactoring, extracting them into a method.

And: This reduces the stack size and number of locals. This reduces or eliminates the performance loss.