3

This question is mostly related to the way language implementer do implements static class initalization (pretty specialized question). It is for curiosity and also to potentially improve my code to gain few nanoseconds

For most object oriented language I know (C++, C#, Java), there is a possibility to define a static constructor and/or static member definition with initialization values.

A language definition could initialize class level static parts:

  • First use of a dll or exe (module load) and in module order of compilation (I think C++)
  • First usage of the class. (I think C# and Java)
  • Potentially other obscurs ones ???

I think that experience showed that it is a lot preferable to initialize static parts on first usage of the object in order to ensure proper precedence of dependencies between classes.

But using static parts initialization on first usage of a class would appears to me to add a hit in performance to any class. That is because on: each call to a constructor, or each call to a static member or functions, the language will have to check if the static initializer has been run prior to give access to them.

So, is there a way (a twist), a language implementation could do to prevent this performance hit, and if yes how?

In the same time... Depending on implementation, can we get class performance improvement by not using any static parts (member, constructor) for that class? I mean compiler will know that for that class, runtime does not have to check if static initalizer has already been call.

Just to be more clear, to ask in another way:

The problem with static initalization is because it need to be called only once for the lifetime of an application and only once. In modern language (C# and Java), it is called on first usage of that class (any usage, static member usage or instance constructor) could trigger the static initialization. But it has to be done only once. So does the compiler check that on each and every static access to that class and/or to any constructor call? Or does the compiler does a magic trick to prevent that (check on each and every call to static member and/or constructor call)?

6
  • Does this answer your question? Is micro-optimisation important when coding?
    – gnat
    Commented Jul 1, 2021 at 14:32
  • There is a relation but my question is more specific. I would really like to know if there is a twist of not for static initialization (performance issue) and/or if not using static stuff could give us better performance. Commented Jul 1, 2021 at 14:36
  • Ultimately, this will depend on the specific implementation of the language (and the version of the implementation), not on the language itself.
    – Doc Brown
    Commented Jul 1, 2021 at 15:32
  • 5
    "would appear" and "performance hit" do not go together. Humans have repeatedly proved to be incapable of predicting the effect of programming language constructs on performance. You must measure under realistic conditions to answer this kind of question. Commented Jul 1, 2021 at 16:14
  • 2
    There are a variety of techniques that can be used to reduce or eliminate any hit after the statics have been initialized, particularly in a JIT compiled language. This includes updating function pointers (from a call to handle the statics to one that skips it) or (re)generating code to bypass the code that does the static initialization/construction. Commented Jul 1, 2021 at 16:28

3 Answers 3

2

It depends on how the language can leverage it's execution environment. There are 4 main classes of static construction: constant/compile time evaluation, assembly level loading, class based lazy loading, and first use based loading. Each has different requirements and costs.

Constant/compile time evaluation

From a runtime perspective, this is the simplest option, the compiler prepares a constant block of bytes as part of the compilation process, and the loader just has to copy it into memory, either as part of assembly level load, or lazily using OS provided memory access primitives. What you can initialize is limited by the language and the need to construct the values ahead of time.

Assembly level loading

This is used by non-local statics in C++. The code is run by the loader at the point the assembly is loaded into the process. This causes some issues, as the initialization code is run in an environment where it is undefined as to whether other statics have been initialized yet. However, once loading is complete all other code does not need to check for initialization, as by definition if loading is complete, initialization is complete.

Class based lazy loading

This is used by the JVM and CLR. The initialisation is called at the first use of the class. This prevents ordering issues, as so long as there is not a cycle, if a constructor depends on a field that itself has static initialization, the runtime will pause the first constructor on its first use of the field, to run the dependent initialization code. The downside is that implemented naively, this would require lots of checks. However the major implementations avoid that by using JIT compilation to avoid that cost. When the calling code is first generated, a thunk is generated which will start loading the clas when called. Once loading and initialization is complete, that thunk is replaced with a call to the actual class, which is guaranteed to have already been initialized, so the class code does not worry about initialization. *

First use based lazy loading

This is used by local statics in C++ and the lazy_static package in rust. In this case, a check is inserted on every use to check if the initialisation has happened, which does have a small runtime cost.

* I am for the purpose of simplification ignoring the complex process where a function may be executed by calling into the interpreter, rather than generated code. The point is in the process of loading class code into an executable state, the runtime initialization has happened, and replaces code that triggers loading/initialisation.

1
  • WOW !!!! I did not expect a so complete and clear answer. Thank you very much!!!!! Commented Jul 5, 2021 at 1:45
2

That is because on: each call to a constructor, or each call to a static member or functions, the language will have to check if the static initializer has been run prior to give access to them.

I'm going to only talk about Java here because I am confident in my understanding of how this works and I know where the documentation is for that. I expect C# and C++ might have different details but the same top-level answer for your question.

Class initialization is described in the JLS section 12.4. I'm looking at the version 11 spec.

First, it discusses when initialization occurs which is immediately before the first time any of the following happens:

  • an instance of the class is created i.e. one of its constructors is called.
  • A static method of the class is called.
  • A static field of the class is assigned.
  • A non-constant static field of the class is referenced.

Next it describes how the initialization status is tracked. The Class object the represents the class contains state that indicates one of 4 situations:

  1. not intialized
  2. currently being initialized
  3. fully initialized
  4. initialization was attempted but failed

We can ignore states 2 and 4 for this answer. We'll just consider and just think of this state as saying whether the class is initialized. This state can be used to determine whether a class needs to be loaded.

For example, before constructing an object or a given class, there will be a check to determine if the class has been initialized. There could be optimizations around this state checking; but they must follow the same semantics. The important thing to understand is that the class will be initialized once and only once.‡

When this first access occurs, the JLS describes the procedure for initializing the class. Much of this is related to thread safety, assertions, etc. which I am going to ignore for simplicity:

  1. Initialize and parent classes or interfaces that have not been initialized.
  2. execute the class variable initializers and static initializers of the class in textual order, "as though they were a single block".
  3. Set the state on the Class object as initialized

Note that in step 2, it's not required that you have a static block. If you have any static variables, they will be initialized at this time either to an assignment in the definition or their default e.g.: null.

The upshot here is that the class initialization executes when the class is initialized only once and every class access (other than constants) requires that the class be initialized. It makes no difference whether you have a static initializer or even if you have static variables. Every class requires initialization.

‡ I'm assuming a single classloader here. If you have multiple classloaders a class may be loaded more than once but this is outside the scope of this answer.

14
  • Not sure to understand your answer. The problem with static initalization is because it need to be called only once for the lifetime of an application and only once. In modern language (C# and Java), it is called on fist usage of that class (any usage, static member usage or instance constructor) could trigger the static initialization. But it has to be done only once. So does the compiler check that on each and every static access to that class and/or to any constructor call? Or does the compiler does a magic trick to prevent that check on each and every call static member and constructor? Commented Jul 2, 2021 at 0:49
  • I think my question was not clear enough. I added more information in an attempt to be a little be clearer (english is not my first language). Commented Jul 2, 2021 at 0:54
  • @EricOuellet Your edits seem to confirm my understanding of the question. My answer's wording can be improved. I will update and provide some references.
    – JimmyJames
    Commented Jul 2, 2021 at 16:11
  • @EricOuellet I've updated my answer. Let me know if it is still unclear.
    – JimmyJames
    Commented Jul 2, 2021 at 17:28
  • at 12.4 : "Initialization of a class consists of executing its static initializers and the initializers for static fields (class variables) declared in the class." ... So when you say: "It makes no difference whether you have a static initializer or even if you have static variables." I wonder if you are sure about that. I'm actually running a test in C# that I've done an hour ago (not very scientific due to thread switching time) but according to actual results, I would say that, at least for C#, it looklikes to make a real diff when there is static stuffs declared or not. Commented Jul 2, 2021 at 18:12
0

I post this code as reference for C#.

In Debug mode, some compilation optimization are not there and the last column is a bit faster but I don't understand why.

In release, the first column is slower which sounds like their is no usage of thunk on first call (optimization like mentionned in the acccepted answer). It sounds like there is always a constant check, I mean there is always a hit in performance when using any kind of static initialization.

I you ever know, please let met know.

Results in Debug mode:

Average With= 25605, Without=25588, WithoutInline=25597, Plain=24576

Results in Release mode:

Average With= 6395, Without=5329, WithoutInline=5328, Plain=5328

Code:

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Windows;

namespace StaticInitEffect
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Test();
        }

        public class ClassWithStaticInit
        {
            public static void Test() { MessageBox.Show("Test"); } // To prevent compiler from optimise because it is the first method, if ever possible???

            public static int IncreaseVal(int val)
            {
                return ++val;
            }

            public static int Val = 0;

            static ClassWithStaticInit()
            {
                Val = 1;
            }
        }

        public class ClassWithoutStaticInit
        {
            public static void Test() { MessageBox.Show("Test"); } // To prevent compiler from optimise because it is the first method, if ever possible???

            public static int IncreaseVal(int val)
            {
                return ++val;
            }

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public static int IncreaseVal2(int val)
            {
                return ++val;
            }

        }

        public class ClassPlain
        {
            public int IncreaseVal(int val)
            {
                return ++val;
            }
        }


        private void Test()
        {
            int val;
            const int max = int.MaxValue;

            ClassWithStaticInit with = new ClassWithStaticInit();
            ClassWithoutStaticInit without = new ClassWithoutStaticInit();
            ClassPlain plain = new ClassPlain();

            long millisecWith = 0;
            long millisecWithout = 0;
            long millisecWithoutInline = 0;
            long millisecPlain = 0;

            for (int n = 0; n < 100; n++)
            {
                Stopwatch sw1 = new Stopwatch();
                sw1.Start();
                val = int.MinValue;
                while (val < max)
                {
                    val = ClassWithStaticInit.IncreaseVal(val);
                }
                sw1.Stop();

                Stopwatch sw2 = new Stopwatch();
                sw2.Start();
                val = int.MinValue;
                while (val < max)
                {
                    val = ClassWithoutStaticInit.IncreaseVal(val);
                }
                sw2.Stop();

                Stopwatch sw3 = new Stopwatch();
                sw3.Start();
                val = int.MinValue;
                while (val < max)
                {
                    val = ClassWithoutStaticInit.IncreaseVal(val);
                }
                sw3.Stop();

                Stopwatch sw4 = new Stopwatch();
                sw4.Start();
                val = int.MinValue;
                while (val < max)
                {
                    val = plain.IncreaseVal(val);
                }
                sw4.Stop();

                Debug.WriteLine($"With= {sw1.ElapsedMilliseconds}, Without={sw2.ElapsedMilliseconds}, WithoutInline={sw3.ElapsedMilliseconds}, Plain={sw4.ElapsedMilliseconds}");
                millisecWith += sw1.ElapsedMilliseconds;
                millisecWithout += sw2.ElapsedMilliseconds;
                millisecWithoutInline += sw3.ElapsedMilliseconds;
                millisecPlain += sw4.ElapsedMilliseconds;
            }

            Debug.WriteLine("Average");
            Debug.WriteLine($"With= {millisecWith / 100}, Without={millisecWithout / 100}, WithoutInline={millisecWithoutInline / 100}, Plain={millisecPlain / 100}");
        }
    }
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.