Static constructor performance and why we cannot specify beforefieldinit

I ran into a speed difference using the following two structures:

public struct NoStaticCtor { private static int _myValue = 3; public static int GetMyValue() { return _myValue; } } public struct StaticCtor { private static int _myValue; public static int GetMyValue() { return _myValue; } static StaticCtor() { _myValue = 3; } } class Program { static void Main(string[] args) { long numTimes = 5000000000; // yup, 5 billion Stopwatch sw = new Stopwatch(); sw.Start(); for (long i = 0; i < numTimes; i++) { NoStaticCtor.GetMyValue(); } sw.Stop(); Console.WriteLine("No static ctor: {0}", sw.Elapsed); sw.Restart(); for (long i = 0; i < numTimes; i++) { StaticCtor.GetMyValue(); } sw.Stop(); Console.WriteLine("with static ctor: {0}", sw.Elapsed); } } 

What gives the results:

 Release (x86), no debugger attached: No static ctor: 00:00:05.1111786 with static ctor: 00:00:09.9502592 Release (x64), no debugger attached: No static ctor: 00:00:03.2595979 with static ctor: 00:00:14.5922220 

The compiler creates a static constructor for NoStaticCtor , which is identical to the one explicitly specified in StaticCtor . I understand that the compiler will only generate beforefieldinit when the static constructor is not explicitly defined.

They produce an almost identical il code, except for one difference, declaring the structure with beforefieldinit , where I feel that the difference lies, because I know that it determines when the type constructor is called, although I cannot understand exactly why there is such a difference. Suppose that it does not call the type constructor at each iteration, since the type constructor can only be called once. 1

So,

1) Why is there a time difference between a structure with and without beforefieldinit ? (I suppose that JITer does something extra in the for loop, however I don't know how to look at the output of JITer to find out what.

2) Why did the compiler developers a) not make all beforefieldinit structures default and b) not allow developers to explicitly indicate this behavior? Of course, this suggests that you cannot, because I could not find a way.


Edit:

1 . I changed the code , essentially running each cycle a second time, expecting improvement, but that wasn't much:

 No static ctor: 00:00:03.3342359 with static ctor: 00:00:14.6139917 No static ctor: 00:00:03.2229995 with static ctor: 00:00:12.9524860 Press any key to continue . . . 

I did this because, although it may be, unlikely, JITer actually called the type constructor at each iteration. It seems to me that JITer knew that the type constructor was already called and did not emit code to do this when the second loop was compiled.

In addition to Motti's answer: This code gives better results, due to differences in JITing, JITing DoSecondLoop does not generate a static ctor check, as it discovered this was done earlier in DoFirstLoop , forcing each cycle to work at the same speed. (~ 3 seconds)

+6
source share
1 answer

The first time you call the type, you must execute a static ctor (regardless of whether it is generated explicitly or implicitly).

When the JIT compiler compiles IL into its own instructions, it checks whether the static ctor for this type has been executed and if it does not emit its own code, which checks (again) whether the static ctor was executed, and if not, executes it.

The coded code is cached for future calls of the same method (and therefore the code had to check again if a static ctor was executed).

The fact is that if the jitted code that checks the static ctor is in a loop, this test will occur at each iteration.

With prefieldinit, the JIT compiler is allowed to optimize the code by testing the static ctor call before entering the loop. If it is absent, this optimization is not allowed.

The C # compiler automatically decides when to release this attribute. It currently (C # 4) emits it only if the code does NOT explicitly define a static constructor. The idea behind this is that if you yourself have defined a static ctor, time may be more important to you, and ststic ctor should not run ahead of time. This may or may not be true, but you cannot change this behavior.

Here is a link to part of my online .NET tutorial that explains this in detail: http://motti.me/c1L

By the way, I do not recommend using static ctors for structs, because believe it or not, they are not guaranteed! This was not part of the question, so I will not dwell on it in detail, but look at this in more detail if you are interested: http://motti.me/c1I (I touch on the topic at about 2:30 in the video).

Hope this helps!

+10
source

Source: https://habr.com/ru/post/901644/


All Articles