Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Code in static constructor runs slower

I noticed that start up times would vary depending on here I placed a piece of initialization code. I thought this was really weird, so I wrote up a small benchmark, which confirmed my suspicions. It seems that code that's executed before the main method is invoked is slower than normal.

Why does Benchmark(); run at different speeds depending on if called before and after the regular code path?

Here's the benchmark code:

class Program {
    static Stopwatch stopwatch = new Stopwatch();
    static Program program = new Program();

    static void Main() {
        Console.WriteLine("main method:");
        Benchmark();
        Console.WriteLine();

        new Program();
    }

    static Program() {
        Console.WriteLine("static constructor:");
        Benchmark();
        Console.WriteLine();
    }

    public Program() {
        Console.WriteLine("public constructor:");
        Benchmark();
        Console.WriteLine();
    }

    static void Benchmark() {
        for (int t = 0; t < 5; t++) {
            stopwatch.Reset();
            stopwatch.Start();
            for (int i = 0; i < 1000000; i++)
                IsPrime(2 * i + 1);
            stopwatch.Stop();
            Console.WriteLine(stopwatch.ElapsedMilliseconds + " ms");
        }
    }

    static Boolean IsPrime(int x) {
        if ((x & 1) == 0)
            return x == 2;
        if (x < 2)
            return false;
        for (int i = 3, s = (int)Math.Sqrt(x); i <= s; i += 2)
            if (x % i == 0)
                return false;
        return true;
    }
}

The results show that Benchmark() runs almost twice slower for both the static constructor and constructor for the static Program program property:

// static Program program = new Program()
public constructor:
894 ms
895 ms
887 ms
884 ms
883 ms

static constructor:
880 ms
872 ms
876 ms
876 ms
872 ms

main method:
426 ms
428 ms
426 ms
426 ms
426 ms

// new Program() in Main()
public constructor:
426 ms
427 ms
426 ms
426 ms
426 ms

Doubling the number of iterations in the benchmark loop causes all times to double, suggesting that the performance penalty incurred is not constant, but a factor.

// static Program program = new Program()
public constructor:
2039 ms
2024 ms
2020 ms
2019 ms
2013 ms

static constructor:
2019 ms
2028 ms
2019 ms
2021 ms
2020 ms

main method:
1120 ms
1120 ms
1119 ms
1120 ms
1120 ms

// new Program() in Main()
public constructor:
1120 ms
1128 ms
1124 ms
1120 ms
1122 ms

Why would this be the case? It would make sense if initialization would be just as fast if it were done where it belongs. Testing was done in .NET 4, release mode, optimizations on.

like image 822
Zong Avatar asked Jul 13 '12 03:07

Zong


2 Answers

This is a very interesting issue. I spent some time experimenting with variants of your program. Here are a few observations:

  1. If you move the Benchmark() static method into a different class, the performance penalty for the static constructor disappears.

  2. If you make the Benchmark() method into an instance method, the performance penalty disappears.

  3. When I profile your fast cases (1, 2) and your slow cases (3, 4), the slow cases spent the extra time in CLR helper methods, in particular JIT_GetSharedNonGCStaticBase_Helper.

Based on this information, I can speculate on what's going on. The CLR needs to ensure that every static constructor executes at most once. A complication is that the static constructors may form a cycle (e.g., if class A contains a static field of type B and class B contains a static field of type A).

When executing inside a static constructor, the JIT compiler insert checks around some static method calls to prevent potential infinite cycles due to cyclic class dependencies. Once the static method is called from outside of a static constructor, the CLR recompiles the method to remove the checks.

This should be pretty close to what's going on.

like image 62
Igor ostrovsky Avatar answered Oct 25 '22 12:10

Igor ostrovsky


This is a very well documented fact.

Static constructors are slow. The .net runtime is not smart enough to optimize them.

refer: Performance penalty of static constructors

explicit static constructors are expensive because they require that the runtime to insure that the value is set exactly before any member of the class is accessed. The exact cost is depends on the scenario, but it can be quite noticeable in some cases.

like image 31
nunespascal Avatar answered Oct 25 '22 13:10

nunespascal