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.
This is a very interesting issue. I spent some time experimenting with variants of your program. Here are a few observations:
If you move the Benchmark() static method into a different class, the performance penalty for the static constructor disappears.
If you make the Benchmark() method into an instance method, the performance penalty disappears.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With