Is access to a static function variable slower than access to a global variable?

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
22
down vote

favorite
5












Static local variables are initialised on the first function call:




Variables declared at block scope with the specifier static have static storage duration but are initialized the first time control passes through their declaration (unless their initialization is zero- or constant-initialization, which can be performed before the block is first entered). On all further calls, the declaration is skipped.




Also, in C++11 there are even more checks:




If multiple threads attempt to initialize the same static local variable concurrently, the initialization occurs exactly once (similar behavior can be obtained for arbitrary functions with std::call_once).
Note: usual implementations of this feature use variants of the double-checked locking pattern, which reduces runtime overhead for already-initialized local statics to a single non-atomic boolean comparison. (since C++11)




At the same time, global variables seem to be initialised on program start (though technically only allocation/deallocation is mentioned on cppreference):




static storage duration. The storage for the object is allocated when the program begins and deallocated when the program ends. Only one instance of the object exists. All objects declared at namespace scope (including global namespace) have this storage duration, plus those declared with static or extern.




So given the following example:



struct A 
// complex type...
;
const A& f()

static A local;
return local;


A global;
const A& g()

return global;



am I correct to assume that f() has to check whether its variable was initialised every time it is called and thus f() will be slower than g()?










share|improve this question



















  • 3




    All correct. If you want to avoid the repeated check, you can use a schwartz counter: en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Nifty_Counter
    – Richard Hodges
    Sep 6 at 7:19






  • 2




    I think this is the proposal, you can find a discussion of the performance impact (one extra load, one extra cache hit on a thread local) open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2660.htm
    – wds
    Sep 6 at 7:26










  • I want to add, that with the static local you are a bit "safer" because you have a guaranteed constructed object when accessing it via the function f(). this is not necessarily the case with the global object (with or without the nifty/schwarz counter). If another global object wants to access your A global and its initialized before your global, than you may run into hard to find errors.
    – phön
    Sep 6 at 8:59











  • @phön, as far as I understand consistent use of nifty counter solves initialisation order problem for global variables.
    – Dev Null
    Sep 13 at 2:15










  • @DevNull Its purpose is to guard, but you can still run into a problem: wandbox.org/permlink/BMdadbrGDyr4rnAI In this example Bar shall be guarded by the niffty counter. Bar holds an integer. In Bars ctor we set it to 42. Since all global static memory gets cleared to zero in the start, the integer will hold zero before the bar ctor was executed. Foo is a class which uses bar, but does not include bar in its own header. In src1.cpp i added a global static Foo. Its not defined which cpp file gets "executed" first, so its src1.cpp vs bar.cpp. When bar.cpp was first, all fine...
    – phön
    Sep 13 at 6:11














up vote
22
down vote

favorite
5












Static local variables are initialised on the first function call:




Variables declared at block scope with the specifier static have static storage duration but are initialized the first time control passes through their declaration (unless their initialization is zero- or constant-initialization, which can be performed before the block is first entered). On all further calls, the declaration is skipped.




Also, in C++11 there are even more checks:




If multiple threads attempt to initialize the same static local variable concurrently, the initialization occurs exactly once (similar behavior can be obtained for arbitrary functions with std::call_once).
Note: usual implementations of this feature use variants of the double-checked locking pattern, which reduces runtime overhead for already-initialized local statics to a single non-atomic boolean comparison. (since C++11)




At the same time, global variables seem to be initialised on program start (though technically only allocation/deallocation is mentioned on cppreference):




static storage duration. The storage for the object is allocated when the program begins and deallocated when the program ends. Only one instance of the object exists. All objects declared at namespace scope (including global namespace) have this storage duration, plus those declared with static or extern.




So given the following example:



struct A 
// complex type...
;
const A& f()

static A local;
return local;


A global;
const A& g()

return global;



am I correct to assume that f() has to check whether its variable was initialised every time it is called and thus f() will be slower than g()?










share|improve this question



















  • 3




    All correct. If you want to avoid the repeated check, you can use a schwartz counter: en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Nifty_Counter
    – Richard Hodges
    Sep 6 at 7:19






  • 2




    I think this is the proposal, you can find a discussion of the performance impact (one extra load, one extra cache hit on a thread local) open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2660.htm
    – wds
    Sep 6 at 7:26










  • I want to add, that with the static local you are a bit "safer" because you have a guaranteed constructed object when accessing it via the function f(). this is not necessarily the case with the global object (with or without the nifty/schwarz counter). If another global object wants to access your A global and its initialized before your global, than you may run into hard to find errors.
    – phön
    Sep 6 at 8:59











  • @phön, as far as I understand consistent use of nifty counter solves initialisation order problem for global variables.
    – Dev Null
    Sep 13 at 2:15










  • @DevNull Its purpose is to guard, but you can still run into a problem: wandbox.org/permlink/BMdadbrGDyr4rnAI In this example Bar shall be guarded by the niffty counter. Bar holds an integer. In Bars ctor we set it to 42. Since all global static memory gets cleared to zero in the start, the integer will hold zero before the bar ctor was executed. Foo is a class which uses bar, but does not include bar in its own header. In src1.cpp i added a global static Foo. Its not defined which cpp file gets "executed" first, so its src1.cpp vs bar.cpp. When bar.cpp was first, all fine...
    – phön
    Sep 13 at 6:11












up vote
22
down vote

favorite
5









up vote
22
down vote

favorite
5






5





Static local variables are initialised on the first function call:




Variables declared at block scope with the specifier static have static storage duration but are initialized the first time control passes through their declaration (unless their initialization is zero- or constant-initialization, which can be performed before the block is first entered). On all further calls, the declaration is skipped.




Also, in C++11 there are even more checks:




If multiple threads attempt to initialize the same static local variable concurrently, the initialization occurs exactly once (similar behavior can be obtained for arbitrary functions with std::call_once).
Note: usual implementations of this feature use variants of the double-checked locking pattern, which reduces runtime overhead for already-initialized local statics to a single non-atomic boolean comparison. (since C++11)




At the same time, global variables seem to be initialised on program start (though technically only allocation/deallocation is mentioned on cppreference):




static storage duration. The storage for the object is allocated when the program begins and deallocated when the program ends. Only one instance of the object exists. All objects declared at namespace scope (including global namespace) have this storage duration, plus those declared with static or extern.




So given the following example:



struct A 
// complex type...
;
const A& f()

static A local;
return local;


A global;
const A& g()

return global;



am I correct to assume that f() has to check whether its variable was initialised every time it is called and thus f() will be slower than g()?










share|improve this question















Static local variables are initialised on the first function call:




Variables declared at block scope with the specifier static have static storage duration but are initialized the first time control passes through their declaration (unless their initialization is zero- or constant-initialization, which can be performed before the block is first entered). On all further calls, the declaration is skipped.




Also, in C++11 there are even more checks:




If multiple threads attempt to initialize the same static local variable concurrently, the initialization occurs exactly once (similar behavior can be obtained for arbitrary functions with std::call_once).
Note: usual implementations of this feature use variants of the double-checked locking pattern, which reduces runtime overhead for already-initialized local statics to a single non-atomic boolean comparison. (since C++11)




At the same time, global variables seem to be initialised on program start (though technically only allocation/deallocation is mentioned on cppreference):




static storage duration. The storage for the object is allocated when the program begins and deallocated when the program ends. Only one instance of the object exists. All objects declared at namespace scope (including global namespace) have this storage duration, plus those declared with static or extern.




So given the following example:



struct A 
// complex type...
;
const A& f()

static A local;
return local;


A global;
const A& g()

return global;



am I correct to assume that f() has to check whether its variable was initialised every time it is called and thus f() will be slower than g()?







c++ static global-variables c++17






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Sep 6 at 7:13

























asked Sep 6 at 7:07









Dev Null

1,595721




1,595721







  • 3




    All correct. If you want to avoid the repeated check, you can use a schwartz counter: en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Nifty_Counter
    – Richard Hodges
    Sep 6 at 7:19






  • 2




    I think this is the proposal, you can find a discussion of the performance impact (one extra load, one extra cache hit on a thread local) open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2660.htm
    – wds
    Sep 6 at 7:26










  • I want to add, that with the static local you are a bit "safer" because you have a guaranteed constructed object when accessing it via the function f(). this is not necessarily the case with the global object (with or without the nifty/schwarz counter). If another global object wants to access your A global and its initialized before your global, than you may run into hard to find errors.
    – phön
    Sep 6 at 8:59











  • @phön, as far as I understand consistent use of nifty counter solves initialisation order problem for global variables.
    – Dev Null
    Sep 13 at 2:15










  • @DevNull Its purpose is to guard, but you can still run into a problem: wandbox.org/permlink/BMdadbrGDyr4rnAI In this example Bar shall be guarded by the niffty counter. Bar holds an integer. In Bars ctor we set it to 42. Since all global static memory gets cleared to zero in the start, the integer will hold zero before the bar ctor was executed. Foo is a class which uses bar, but does not include bar in its own header. In src1.cpp i added a global static Foo. Its not defined which cpp file gets "executed" first, so its src1.cpp vs bar.cpp. When bar.cpp was first, all fine...
    – phön
    Sep 13 at 6:11












  • 3




    All correct. If you want to avoid the repeated check, you can use a schwartz counter: en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Nifty_Counter
    – Richard Hodges
    Sep 6 at 7:19






  • 2




    I think this is the proposal, you can find a discussion of the performance impact (one extra load, one extra cache hit on a thread local) open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2660.htm
    – wds
    Sep 6 at 7:26










  • I want to add, that with the static local you are a bit "safer" because you have a guaranteed constructed object when accessing it via the function f(). this is not necessarily the case with the global object (with or without the nifty/schwarz counter). If another global object wants to access your A global and its initialized before your global, than you may run into hard to find errors.
    – phön
    Sep 6 at 8:59











  • @phön, as far as I understand consistent use of nifty counter solves initialisation order problem for global variables.
    – Dev Null
    Sep 13 at 2:15










  • @DevNull Its purpose is to guard, but you can still run into a problem: wandbox.org/permlink/BMdadbrGDyr4rnAI In this example Bar shall be guarded by the niffty counter. Bar holds an integer. In Bars ctor we set it to 42. Since all global static memory gets cleared to zero in the start, the integer will hold zero before the bar ctor was executed. Foo is a class which uses bar, but does not include bar in its own header. In src1.cpp i added a global static Foo. Its not defined which cpp file gets "executed" first, so its src1.cpp vs bar.cpp. When bar.cpp was first, all fine...
    – phön
    Sep 13 at 6:11







3




3




All correct. If you want to avoid the repeated check, you can use a schwartz counter: en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Nifty_Counter
– Richard Hodges
Sep 6 at 7:19




All correct. If you want to avoid the repeated check, you can use a schwartz counter: en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Nifty_Counter
– Richard Hodges
Sep 6 at 7:19




2




2




I think this is the proposal, you can find a discussion of the performance impact (one extra load, one extra cache hit on a thread local) open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2660.htm
– wds
Sep 6 at 7:26




I think this is the proposal, you can find a discussion of the performance impact (one extra load, one extra cache hit on a thread local) open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2660.htm
– wds
Sep 6 at 7:26












I want to add, that with the static local you are a bit "safer" because you have a guaranteed constructed object when accessing it via the function f(). this is not necessarily the case with the global object (with or without the nifty/schwarz counter). If another global object wants to access your A global and its initialized before your global, than you may run into hard to find errors.
– phön
Sep 6 at 8:59





I want to add, that with the static local you are a bit "safer" because you have a guaranteed constructed object when accessing it via the function f(). this is not necessarily the case with the global object (with or without the nifty/schwarz counter). If another global object wants to access your A global and its initialized before your global, than you may run into hard to find errors.
– phön
Sep 6 at 8:59













@phön, as far as I understand consistent use of nifty counter solves initialisation order problem for global variables.
– Dev Null
Sep 13 at 2:15




@phön, as far as I understand consistent use of nifty counter solves initialisation order problem for global variables.
– Dev Null
Sep 13 at 2:15












@DevNull Its purpose is to guard, but you can still run into a problem: wandbox.org/permlink/BMdadbrGDyr4rnAI In this example Bar shall be guarded by the niffty counter. Bar holds an integer. In Bars ctor we set it to 42. Since all global static memory gets cleared to zero in the start, the integer will hold zero before the bar ctor was executed. Foo is a class which uses bar, but does not include bar in its own header. In src1.cpp i added a global static Foo. Its not defined which cpp file gets "executed" first, so its src1.cpp vs bar.cpp. When bar.cpp was first, all fine...
– phön
Sep 13 at 6:11




@DevNull Its purpose is to guard, but you can still run into a problem: wandbox.org/permlink/BMdadbrGDyr4rnAI In this example Bar shall be guarded by the niffty counter. Bar holds an integer. In Bars ctor we set it to 42. Since all global static memory gets cleared to zero in the start, the integer will hold zero before the bar ctor was executed. Foo is a class which uses bar, but does not include bar in its own header. In src1.cpp i added a global static Foo. Its not defined which cpp file gets "executed" first, so its src1.cpp vs bar.cpp. When bar.cpp was first, all fine...
– phön
Sep 13 at 6:11












4 Answers
4






active

oldest

votes

















up vote
14
down vote



accepted










You are conceptually correct of course, but contemporary architectures can deal with this.



A modern compiler and architecture would arrange the pipeline such that the already-initialised branch was assumed. The overhead of initialisation would therefore incur an extra pipeline dump, that's all.



If you're in any doubt, check the assembly.






share|improve this answer
















  • 3




    Exemplary assembly: godbolt.org/z/iK5V-b. Note the movzx eax, BYTE PTR guard variable for f()::x[rip] instruction and the following check performed each time the function is invoked.
    – Daniel Langr
    Sep 6 at 7:23







  • 5




    @DanielLangr - One should also note the jne .L12 which means the bulk of the initialization will only be performed once, even with a basic branch predictor.
    – StoryTeller
    Sep 6 at 7:26











  • @StoryTeller why do you have to mention branch predictor? The initialization will be performed once even without any predictor.
    – Ruslan
    Sep 6 at 12:02






  • 1




    @Ruslan - Yes. The point was to support Bathsheba's point. Even a basic predictor will only pay the major cost (filling the instruction pipeline and then dumping it) once. With no predictor it's unlikely to even try and keep loading instructions into any pipeline, innit?
    – StoryTeller
    Sep 6 at 12:06






  • 1




    And in theory some (especially old) architectures could avoid the overhead by having self-modifying machine code; so that the initialization-test is actually removed after the first time. (Doing it on a modern multi-thread architecture would be challenging.)
    – Hans Olsson
    Sep 6 at 14:11

















up vote
6
down vote













Yes, it is almost certainly slightly slower. Most of the time it will however not matter and the cost will be outweighted by the "logic and style" benefit.



Technically, a function-local static variable is the same as a global variable. Only just that its name is not globally known (which is a good thing), and its initialization is guaranteed to happen not only at an exactly specified time, but also only once, and threadsafe.



This means that a function-local static variable must know whether initialization has happened, and thus needs at least one extra memory access and one conditional jump that the global (in principle) doesn't need. An implemenation may do someting similar for globals, but it needs not (and usually doesn't).



Chances are good that the jump is predicted correctly in all cases but two. The first two calls are highly likely to be predicted wrong (usually jumps are by default assumed to be taken rather than not, wrong assumption on first call, and subsequent jumps are assumed to take the same path as the last one, again wrong). After that, you should be good to go, near 100% correct prediction.

But even a correctly predicted jump isn't free (the CPU can still only start a given number of instructions every cycle, even assuming they take zero time to complete), but it's not much. If the memory latency, which may be a couple of hundred cycles in the worst case can be successfully hidden, the cost almost disappears in pipelining. Also, every access fetches an extra cacheline that wouldn't otherwise be needed (the has-been-initialized flag likely isn't stored in the same cache line as the data). Thus, you have slightly worse L1 performance (L2 should be big enough so you can say "yeah, so what").



It also needs to actually perform something once and threadsafe that the global (in principle) doesn't have to do, at least not in a way that you see. An implementation can do something different, but most just initialize globals before main is entered, and not rarely most of it is done with a memset or implicitly because the variable is stored in a segment that is zeroed anyway.

Your static variable must be initialized when the initialization code is executed, and it must happen in a threadsafe manner. Depending on how much your implementation sucks this can be quite expensive. I decided to forfeit on the thread safety feature and always compile with fno-threadsafe-statics (even if this isn't standard-compliant) after discovering that GCC (which is otherwise an OK allround compiler) would actually lock a mutex for every static initialization.






share|improve this answer



























    up vote
    2
    down vote













    From https://en.cppreference.com/w/cpp/language/initialization




    Deferred dynamic initialization

    It is implementation-defined whether dynamic initialization happens-before the first statement of the main function (for statics) or the initial function of the thread (for thread-locals), or deferred to happen after.



    If the initialization of a non-inline variable (since C++17) is deferred to happen after the first statement of main/thread function, it happens before the first odr-use of any variable with static/thread storage duration defined in the same translation unit as the variable to be initialized.




    So similar check may have to be done also for global variables.



    so f() is not necessary "slower" than g().






    share|improve this answer



























      up vote
      0
      down vote













      g() is not thread-safe, and is susceptible to all sorts of ordering problems. Safety is going to come at a price. There are several ways to pay it:



      f(), the Meyer's Singleton, pays the price on every access. If accessed frequently or accessed during a performance-sensitive section of your code, then it does make sense to avoid f(). Your processor presumably has a finite number of circuits it can devote to branch prediction, and you are being forced to read an atomic variable before the branch anyway. It is a tall price to continually pay for just ensuring that the initialization happened only once.



      h(), described below, works very much like g() with an extra indirection, but assumes that h_init() gets called exactly once at the beginning of execution. Preferably, you would define a subroutine that gets called as the line of main(); that calls every function like h_init(), with an absolute ordering. Hopefully, these objects do not need to be destructed.



      Alternatively, if you use GCC, you can annotate h_init() with __attribute__((constructor)). I prefer the explicitness of the static init subroutine though.



      A * h_global = nullptr;
      void h_init() h_global = new A ;
      A const& h() return *h_global;


      h2() is just like h(), minus the extra indirection:



      alignas(alignof(A)) char h2_global [sizeof(A)] = ;
      void h2_init() new (std::begin(h2_global)) A ;
      A const& h2() return * reinterpret_cast <A const *> (std::cbegin(h2_global));





      share|improve this answer






















      • given that g() accesses global that is constructed before main() is called, I wouldn't worry too much about its thread safety
        – Dev Null
        Sep 12 at 20:25











      • @DevNull There is also the ordering problem: if nontrivial static object x is depended upon to initialize some other static object y in a different translation unit, and y gets initialized first, y may be initialized with a bogus value.
        – KevinZ
        Sep 13 at 1:29










      • true, but this can be worked around with nifty counters.
        – Dev Null
        Sep 13 at 2:12










      • @DevNull Disagree, that trick looks like a disaster waiting to happen. If you already have a specific ordering, you should just make that explicit.
        – KevinZ
        Sep 13 at 17:41










      Your Answer





      StackExchange.ifUsing("editor", function ()
      StackExchange.using("externalEditor", function ()
      StackExchange.using("snippets", function ()
      StackExchange.snippets.init();
      );
      );
      , "code-snippets");

      StackExchange.ready(function()
      var channelOptions =
      tags: "".split(" "),
      id: "1"
      ;
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function()
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled)
      StackExchange.using("snippets", function()
      createEditor();
      );

      else
      createEditor();

      );

      function createEditor()
      StackExchange.prepareEditor(
      heartbeatType: 'answer',
      convertImagesToLinks: true,
      noModals: false,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      );



      );













       

      draft saved


      draft discarded


















      StackExchange.ready(
      function ()
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52198322%2fis-access-to-a-static-function-variable-slower-than-access-to-a-global-variable%23new-answer', 'question_page');

      );

      Post as a guest






























      4 Answers
      4






      active

      oldest

      votes








      4 Answers
      4






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes








      up vote
      14
      down vote



      accepted










      You are conceptually correct of course, but contemporary architectures can deal with this.



      A modern compiler and architecture would arrange the pipeline such that the already-initialised branch was assumed. The overhead of initialisation would therefore incur an extra pipeline dump, that's all.



      If you're in any doubt, check the assembly.






      share|improve this answer
















      • 3




        Exemplary assembly: godbolt.org/z/iK5V-b. Note the movzx eax, BYTE PTR guard variable for f()::x[rip] instruction and the following check performed each time the function is invoked.
        – Daniel Langr
        Sep 6 at 7:23







      • 5




        @DanielLangr - One should also note the jne .L12 which means the bulk of the initialization will only be performed once, even with a basic branch predictor.
        – StoryTeller
        Sep 6 at 7:26











      • @StoryTeller why do you have to mention branch predictor? The initialization will be performed once even without any predictor.
        – Ruslan
        Sep 6 at 12:02






      • 1




        @Ruslan - Yes. The point was to support Bathsheba's point. Even a basic predictor will only pay the major cost (filling the instruction pipeline and then dumping it) once. With no predictor it's unlikely to even try and keep loading instructions into any pipeline, innit?
        – StoryTeller
        Sep 6 at 12:06






      • 1




        And in theory some (especially old) architectures could avoid the overhead by having self-modifying machine code; so that the initialization-test is actually removed after the first time. (Doing it on a modern multi-thread architecture would be challenging.)
        – Hans Olsson
        Sep 6 at 14:11














      up vote
      14
      down vote



      accepted










      You are conceptually correct of course, but contemporary architectures can deal with this.



      A modern compiler and architecture would arrange the pipeline such that the already-initialised branch was assumed. The overhead of initialisation would therefore incur an extra pipeline dump, that's all.



      If you're in any doubt, check the assembly.






      share|improve this answer
















      • 3




        Exemplary assembly: godbolt.org/z/iK5V-b. Note the movzx eax, BYTE PTR guard variable for f()::x[rip] instruction and the following check performed each time the function is invoked.
        – Daniel Langr
        Sep 6 at 7:23







      • 5




        @DanielLangr - One should also note the jne .L12 which means the bulk of the initialization will only be performed once, even with a basic branch predictor.
        – StoryTeller
        Sep 6 at 7:26











      • @StoryTeller why do you have to mention branch predictor? The initialization will be performed once even without any predictor.
        – Ruslan
        Sep 6 at 12:02






      • 1




        @Ruslan - Yes. The point was to support Bathsheba's point. Even a basic predictor will only pay the major cost (filling the instruction pipeline and then dumping it) once. With no predictor it's unlikely to even try and keep loading instructions into any pipeline, innit?
        – StoryTeller
        Sep 6 at 12:06






      • 1




        And in theory some (especially old) architectures could avoid the overhead by having self-modifying machine code; so that the initialization-test is actually removed after the first time. (Doing it on a modern multi-thread architecture would be challenging.)
        – Hans Olsson
        Sep 6 at 14:11












      up vote
      14
      down vote



      accepted







      up vote
      14
      down vote



      accepted






      You are conceptually correct of course, but contemporary architectures can deal with this.



      A modern compiler and architecture would arrange the pipeline such that the already-initialised branch was assumed. The overhead of initialisation would therefore incur an extra pipeline dump, that's all.



      If you're in any doubt, check the assembly.






      share|improve this answer












      You are conceptually correct of course, but contemporary architectures can deal with this.



      A modern compiler and architecture would arrange the pipeline such that the already-initialised branch was assumed. The overhead of initialisation would therefore incur an extra pipeline dump, that's all.



      If you're in any doubt, check the assembly.







      share|improve this answer












      share|improve this answer



      share|improve this answer










      answered Sep 6 at 7:14









      Bathsheba

      168k26238353




      168k26238353







      • 3




        Exemplary assembly: godbolt.org/z/iK5V-b. Note the movzx eax, BYTE PTR guard variable for f()::x[rip] instruction and the following check performed each time the function is invoked.
        – Daniel Langr
        Sep 6 at 7:23







      • 5




        @DanielLangr - One should also note the jne .L12 which means the bulk of the initialization will only be performed once, even with a basic branch predictor.
        – StoryTeller
        Sep 6 at 7:26











      • @StoryTeller why do you have to mention branch predictor? The initialization will be performed once even without any predictor.
        – Ruslan
        Sep 6 at 12:02






      • 1




        @Ruslan - Yes. The point was to support Bathsheba's point. Even a basic predictor will only pay the major cost (filling the instruction pipeline and then dumping it) once. With no predictor it's unlikely to even try and keep loading instructions into any pipeline, innit?
        – StoryTeller
        Sep 6 at 12:06






      • 1




        And in theory some (especially old) architectures could avoid the overhead by having self-modifying machine code; so that the initialization-test is actually removed after the first time. (Doing it on a modern multi-thread architecture would be challenging.)
        – Hans Olsson
        Sep 6 at 14:11












      • 3




        Exemplary assembly: godbolt.org/z/iK5V-b. Note the movzx eax, BYTE PTR guard variable for f()::x[rip] instruction and the following check performed each time the function is invoked.
        – Daniel Langr
        Sep 6 at 7:23







      • 5




        @DanielLangr - One should also note the jne .L12 which means the bulk of the initialization will only be performed once, even with a basic branch predictor.
        – StoryTeller
        Sep 6 at 7:26











      • @StoryTeller why do you have to mention branch predictor? The initialization will be performed once even without any predictor.
        – Ruslan
        Sep 6 at 12:02






      • 1




        @Ruslan - Yes. The point was to support Bathsheba's point. Even a basic predictor will only pay the major cost (filling the instruction pipeline and then dumping it) once. With no predictor it's unlikely to even try and keep loading instructions into any pipeline, innit?
        – StoryTeller
        Sep 6 at 12:06






      • 1




        And in theory some (especially old) architectures could avoid the overhead by having self-modifying machine code; so that the initialization-test is actually removed after the first time. (Doing it on a modern multi-thread architecture would be challenging.)
        – Hans Olsson
        Sep 6 at 14:11







      3




      3




      Exemplary assembly: godbolt.org/z/iK5V-b. Note the movzx eax, BYTE PTR guard variable for f()::x[rip] instruction and the following check performed each time the function is invoked.
      – Daniel Langr
      Sep 6 at 7:23





      Exemplary assembly: godbolt.org/z/iK5V-b. Note the movzx eax, BYTE PTR guard variable for f()::x[rip] instruction and the following check performed each time the function is invoked.
      – Daniel Langr
      Sep 6 at 7:23





      5




      5




      @DanielLangr - One should also note the jne .L12 which means the bulk of the initialization will only be performed once, even with a basic branch predictor.
      – StoryTeller
      Sep 6 at 7:26





      @DanielLangr - One should also note the jne .L12 which means the bulk of the initialization will only be performed once, even with a basic branch predictor.
      – StoryTeller
      Sep 6 at 7:26













      @StoryTeller why do you have to mention branch predictor? The initialization will be performed once even without any predictor.
      – Ruslan
      Sep 6 at 12:02




      @StoryTeller why do you have to mention branch predictor? The initialization will be performed once even without any predictor.
      – Ruslan
      Sep 6 at 12:02




      1




      1




      @Ruslan - Yes. The point was to support Bathsheba's point. Even a basic predictor will only pay the major cost (filling the instruction pipeline and then dumping it) once. With no predictor it's unlikely to even try and keep loading instructions into any pipeline, innit?
      – StoryTeller
      Sep 6 at 12:06




      @Ruslan - Yes. The point was to support Bathsheba's point. Even a basic predictor will only pay the major cost (filling the instruction pipeline and then dumping it) once. With no predictor it's unlikely to even try and keep loading instructions into any pipeline, innit?
      – StoryTeller
      Sep 6 at 12:06




      1




      1




      And in theory some (especially old) architectures could avoid the overhead by having self-modifying machine code; so that the initialization-test is actually removed after the first time. (Doing it on a modern multi-thread architecture would be challenging.)
      – Hans Olsson
      Sep 6 at 14:11




      And in theory some (especially old) architectures could avoid the overhead by having self-modifying machine code; so that the initialization-test is actually removed after the first time. (Doing it on a modern multi-thread architecture would be challenging.)
      – Hans Olsson
      Sep 6 at 14:11












      up vote
      6
      down vote













      Yes, it is almost certainly slightly slower. Most of the time it will however not matter and the cost will be outweighted by the "logic and style" benefit.



      Technically, a function-local static variable is the same as a global variable. Only just that its name is not globally known (which is a good thing), and its initialization is guaranteed to happen not only at an exactly specified time, but also only once, and threadsafe.



      This means that a function-local static variable must know whether initialization has happened, and thus needs at least one extra memory access and one conditional jump that the global (in principle) doesn't need. An implemenation may do someting similar for globals, but it needs not (and usually doesn't).



      Chances are good that the jump is predicted correctly in all cases but two. The first two calls are highly likely to be predicted wrong (usually jumps are by default assumed to be taken rather than not, wrong assumption on first call, and subsequent jumps are assumed to take the same path as the last one, again wrong). After that, you should be good to go, near 100% correct prediction.

      But even a correctly predicted jump isn't free (the CPU can still only start a given number of instructions every cycle, even assuming they take zero time to complete), but it's not much. If the memory latency, which may be a couple of hundred cycles in the worst case can be successfully hidden, the cost almost disappears in pipelining. Also, every access fetches an extra cacheline that wouldn't otherwise be needed (the has-been-initialized flag likely isn't stored in the same cache line as the data). Thus, you have slightly worse L1 performance (L2 should be big enough so you can say "yeah, so what").



      It also needs to actually perform something once and threadsafe that the global (in principle) doesn't have to do, at least not in a way that you see. An implementation can do something different, but most just initialize globals before main is entered, and not rarely most of it is done with a memset or implicitly because the variable is stored in a segment that is zeroed anyway.

      Your static variable must be initialized when the initialization code is executed, and it must happen in a threadsafe manner. Depending on how much your implementation sucks this can be quite expensive. I decided to forfeit on the thread safety feature and always compile with fno-threadsafe-statics (even if this isn't standard-compliant) after discovering that GCC (which is otherwise an OK allround compiler) would actually lock a mutex for every static initialization.






      share|improve this answer
























        up vote
        6
        down vote













        Yes, it is almost certainly slightly slower. Most of the time it will however not matter and the cost will be outweighted by the "logic and style" benefit.



        Technically, a function-local static variable is the same as a global variable. Only just that its name is not globally known (which is a good thing), and its initialization is guaranteed to happen not only at an exactly specified time, but also only once, and threadsafe.



        This means that a function-local static variable must know whether initialization has happened, and thus needs at least one extra memory access and one conditional jump that the global (in principle) doesn't need. An implemenation may do someting similar for globals, but it needs not (and usually doesn't).



        Chances are good that the jump is predicted correctly in all cases but two. The first two calls are highly likely to be predicted wrong (usually jumps are by default assumed to be taken rather than not, wrong assumption on first call, and subsequent jumps are assumed to take the same path as the last one, again wrong). After that, you should be good to go, near 100% correct prediction.

        But even a correctly predicted jump isn't free (the CPU can still only start a given number of instructions every cycle, even assuming they take zero time to complete), but it's not much. If the memory latency, which may be a couple of hundred cycles in the worst case can be successfully hidden, the cost almost disappears in pipelining. Also, every access fetches an extra cacheline that wouldn't otherwise be needed (the has-been-initialized flag likely isn't stored in the same cache line as the data). Thus, you have slightly worse L1 performance (L2 should be big enough so you can say "yeah, so what").



        It also needs to actually perform something once and threadsafe that the global (in principle) doesn't have to do, at least not in a way that you see. An implementation can do something different, but most just initialize globals before main is entered, and not rarely most of it is done with a memset or implicitly because the variable is stored in a segment that is zeroed anyway.

        Your static variable must be initialized when the initialization code is executed, and it must happen in a threadsafe manner. Depending on how much your implementation sucks this can be quite expensive. I decided to forfeit on the thread safety feature and always compile with fno-threadsafe-statics (even if this isn't standard-compliant) after discovering that GCC (which is otherwise an OK allround compiler) would actually lock a mutex for every static initialization.






        share|improve this answer






















          up vote
          6
          down vote










          up vote
          6
          down vote









          Yes, it is almost certainly slightly slower. Most of the time it will however not matter and the cost will be outweighted by the "logic and style" benefit.



          Technically, a function-local static variable is the same as a global variable. Only just that its name is not globally known (which is a good thing), and its initialization is guaranteed to happen not only at an exactly specified time, but also only once, and threadsafe.



          This means that a function-local static variable must know whether initialization has happened, and thus needs at least one extra memory access and one conditional jump that the global (in principle) doesn't need. An implemenation may do someting similar for globals, but it needs not (and usually doesn't).



          Chances are good that the jump is predicted correctly in all cases but two. The first two calls are highly likely to be predicted wrong (usually jumps are by default assumed to be taken rather than not, wrong assumption on first call, and subsequent jumps are assumed to take the same path as the last one, again wrong). After that, you should be good to go, near 100% correct prediction.

          But even a correctly predicted jump isn't free (the CPU can still only start a given number of instructions every cycle, even assuming they take zero time to complete), but it's not much. If the memory latency, which may be a couple of hundred cycles in the worst case can be successfully hidden, the cost almost disappears in pipelining. Also, every access fetches an extra cacheline that wouldn't otherwise be needed (the has-been-initialized flag likely isn't stored in the same cache line as the data). Thus, you have slightly worse L1 performance (L2 should be big enough so you can say "yeah, so what").



          It also needs to actually perform something once and threadsafe that the global (in principle) doesn't have to do, at least not in a way that you see. An implementation can do something different, but most just initialize globals before main is entered, and not rarely most of it is done with a memset or implicitly because the variable is stored in a segment that is zeroed anyway.

          Your static variable must be initialized when the initialization code is executed, and it must happen in a threadsafe manner. Depending on how much your implementation sucks this can be quite expensive. I decided to forfeit on the thread safety feature and always compile with fno-threadsafe-statics (even if this isn't standard-compliant) after discovering that GCC (which is otherwise an OK allround compiler) would actually lock a mutex for every static initialization.






          share|improve this answer












          Yes, it is almost certainly slightly slower. Most of the time it will however not matter and the cost will be outweighted by the "logic and style" benefit.



          Technically, a function-local static variable is the same as a global variable. Only just that its name is not globally known (which is a good thing), and its initialization is guaranteed to happen not only at an exactly specified time, but also only once, and threadsafe.



          This means that a function-local static variable must know whether initialization has happened, and thus needs at least one extra memory access and one conditional jump that the global (in principle) doesn't need. An implemenation may do someting similar for globals, but it needs not (and usually doesn't).



          Chances are good that the jump is predicted correctly in all cases but two. The first two calls are highly likely to be predicted wrong (usually jumps are by default assumed to be taken rather than not, wrong assumption on first call, and subsequent jumps are assumed to take the same path as the last one, again wrong). After that, you should be good to go, near 100% correct prediction.

          But even a correctly predicted jump isn't free (the CPU can still only start a given number of instructions every cycle, even assuming they take zero time to complete), but it's not much. If the memory latency, which may be a couple of hundred cycles in the worst case can be successfully hidden, the cost almost disappears in pipelining. Also, every access fetches an extra cacheline that wouldn't otherwise be needed (the has-been-initialized flag likely isn't stored in the same cache line as the data). Thus, you have slightly worse L1 performance (L2 should be big enough so you can say "yeah, so what").



          It also needs to actually perform something once and threadsafe that the global (in principle) doesn't have to do, at least not in a way that you see. An implementation can do something different, but most just initialize globals before main is entered, and not rarely most of it is done with a memset or implicitly because the variable is stored in a segment that is zeroed anyway.

          Your static variable must be initialized when the initialization code is executed, and it must happen in a threadsafe manner. Depending on how much your implementation sucks this can be quite expensive. I decided to forfeit on the thread safety feature and always compile with fno-threadsafe-statics (even if this isn't standard-compliant) after discovering that GCC (which is otherwise an OK allround compiler) would actually lock a mutex for every static initialization.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Sep 6 at 10:28









          Damon

          49.3k1493151




          49.3k1493151




















              up vote
              2
              down vote













              From https://en.cppreference.com/w/cpp/language/initialization




              Deferred dynamic initialization

              It is implementation-defined whether dynamic initialization happens-before the first statement of the main function (for statics) or the initial function of the thread (for thread-locals), or deferred to happen after.



              If the initialization of a non-inline variable (since C++17) is deferred to happen after the first statement of main/thread function, it happens before the first odr-use of any variable with static/thread storage duration defined in the same translation unit as the variable to be initialized.




              So similar check may have to be done also for global variables.



              so f() is not necessary "slower" than g().






              share|improve this answer
























                up vote
                2
                down vote













                From https://en.cppreference.com/w/cpp/language/initialization




                Deferred dynamic initialization

                It is implementation-defined whether dynamic initialization happens-before the first statement of the main function (for statics) or the initial function of the thread (for thread-locals), or deferred to happen after.



                If the initialization of a non-inline variable (since C++17) is deferred to happen after the first statement of main/thread function, it happens before the first odr-use of any variable with static/thread storage duration defined in the same translation unit as the variable to be initialized.




                So similar check may have to be done also for global variables.



                so f() is not necessary "slower" than g().






                share|improve this answer






















                  up vote
                  2
                  down vote










                  up vote
                  2
                  down vote









                  From https://en.cppreference.com/w/cpp/language/initialization




                  Deferred dynamic initialization

                  It is implementation-defined whether dynamic initialization happens-before the first statement of the main function (for statics) or the initial function of the thread (for thread-locals), or deferred to happen after.



                  If the initialization of a non-inline variable (since C++17) is deferred to happen after the first statement of main/thread function, it happens before the first odr-use of any variable with static/thread storage duration defined in the same translation unit as the variable to be initialized.




                  So similar check may have to be done also for global variables.



                  so f() is not necessary "slower" than g().






                  share|improve this answer












                  From https://en.cppreference.com/w/cpp/language/initialization




                  Deferred dynamic initialization

                  It is implementation-defined whether dynamic initialization happens-before the first statement of the main function (for statics) or the initial function of the thread (for thread-locals), or deferred to happen after.



                  If the initialization of a non-inline variable (since C++17) is deferred to happen after the first statement of main/thread function, it happens before the first odr-use of any variable with static/thread storage duration defined in the same translation unit as the variable to be initialized.




                  So similar check may have to be done also for global variables.



                  so f() is not necessary "slower" than g().







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Sep 6 at 9:45









                  Jarod42

                  107k1297171




                  107k1297171




















                      up vote
                      0
                      down vote













                      g() is not thread-safe, and is susceptible to all sorts of ordering problems. Safety is going to come at a price. There are several ways to pay it:



                      f(), the Meyer's Singleton, pays the price on every access. If accessed frequently or accessed during a performance-sensitive section of your code, then it does make sense to avoid f(). Your processor presumably has a finite number of circuits it can devote to branch prediction, and you are being forced to read an atomic variable before the branch anyway. It is a tall price to continually pay for just ensuring that the initialization happened only once.



                      h(), described below, works very much like g() with an extra indirection, but assumes that h_init() gets called exactly once at the beginning of execution. Preferably, you would define a subroutine that gets called as the line of main(); that calls every function like h_init(), with an absolute ordering. Hopefully, these objects do not need to be destructed.



                      Alternatively, if you use GCC, you can annotate h_init() with __attribute__((constructor)). I prefer the explicitness of the static init subroutine though.



                      A * h_global = nullptr;
                      void h_init() h_global = new A ;
                      A const& h() return *h_global;


                      h2() is just like h(), minus the extra indirection:



                      alignas(alignof(A)) char h2_global [sizeof(A)] = ;
                      void h2_init() new (std::begin(h2_global)) A ;
                      A const& h2() return * reinterpret_cast <A const *> (std::cbegin(h2_global));





                      share|improve this answer






















                      • given that g() accesses global that is constructed before main() is called, I wouldn't worry too much about its thread safety
                        – Dev Null
                        Sep 12 at 20:25











                      • @DevNull There is also the ordering problem: if nontrivial static object x is depended upon to initialize some other static object y in a different translation unit, and y gets initialized first, y may be initialized with a bogus value.
                        – KevinZ
                        Sep 13 at 1:29










                      • true, but this can be worked around with nifty counters.
                        – Dev Null
                        Sep 13 at 2:12










                      • @DevNull Disagree, that trick looks like a disaster waiting to happen. If you already have a specific ordering, you should just make that explicit.
                        – KevinZ
                        Sep 13 at 17:41














                      up vote
                      0
                      down vote













                      g() is not thread-safe, and is susceptible to all sorts of ordering problems. Safety is going to come at a price. There are several ways to pay it:



                      f(), the Meyer's Singleton, pays the price on every access. If accessed frequently or accessed during a performance-sensitive section of your code, then it does make sense to avoid f(). Your processor presumably has a finite number of circuits it can devote to branch prediction, and you are being forced to read an atomic variable before the branch anyway. It is a tall price to continually pay for just ensuring that the initialization happened only once.



                      h(), described below, works very much like g() with an extra indirection, but assumes that h_init() gets called exactly once at the beginning of execution. Preferably, you would define a subroutine that gets called as the line of main(); that calls every function like h_init(), with an absolute ordering. Hopefully, these objects do not need to be destructed.



                      Alternatively, if you use GCC, you can annotate h_init() with __attribute__((constructor)). I prefer the explicitness of the static init subroutine though.



                      A * h_global = nullptr;
                      void h_init() h_global = new A ;
                      A const& h() return *h_global;


                      h2() is just like h(), minus the extra indirection:



                      alignas(alignof(A)) char h2_global [sizeof(A)] = ;
                      void h2_init() new (std::begin(h2_global)) A ;
                      A const& h2() return * reinterpret_cast <A const *> (std::cbegin(h2_global));





                      share|improve this answer






















                      • given that g() accesses global that is constructed before main() is called, I wouldn't worry too much about its thread safety
                        – Dev Null
                        Sep 12 at 20:25











                      • @DevNull There is also the ordering problem: if nontrivial static object x is depended upon to initialize some other static object y in a different translation unit, and y gets initialized first, y may be initialized with a bogus value.
                        – KevinZ
                        Sep 13 at 1:29










                      • true, but this can be worked around with nifty counters.
                        – Dev Null
                        Sep 13 at 2:12










                      • @DevNull Disagree, that trick looks like a disaster waiting to happen. If you already have a specific ordering, you should just make that explicit.
                        – KevinZ
                        Sep 13 at 17:41












                      up vote
                      0
                      down vote










                      up vote
                      0
                      down vote









                      g() is not thread-safe, and is susceptible to all sorts of ordering problems. Safety is going to come at a price. There are several ways to pay it:



                      f(), the Meyer's Singleton, pays the price on every access. If accessed frequently or accessed during a performance-sensitive section of your code, then it does make sense to avoid f(). Your processor presumably has a finite number of circuits it can devote to branch prediction, and you are being forced to read an atomic variable before the branch anyway. It is a tall price to continually pay for just ensuring that the initialization happened only once.



                      h(), described below, works very much like g() with an extra indirection, but assumes that h_init() gets called exactly once at the beginning of execution. Preferably, you would define a subroutine that gets called as the line of main(); that calls every function like h_init(), with an absolute ordering. Hopefully, these objects do not need to be destructed.



                      Alternatively, if you use GCC, you can annotate h_init() with __attribute__((constructor)). I prefer the explicitness of the static init subroutine though.



                      A * h_global = nullptr;
                      void h_init() h_global = new A ;
                      A const& h() return *h_global;


                      h2() is just like h(), minus the extra indirection:



                      alignas(alignof(A)) char h2_global [sizeof(A)] = ;
                      void h2_init() new (std::begin(h2_global)) A ;
                      A const& h2() return * reinterpret_cast <A const *> (std::cbegin(h2_global));





                      share|improve this answer














                      g() is not thread-safe, and is susceptible to all sorts of ordering problems. Safety is going to come at a price. There are several ways to pay it:



                      f(), the Meyer's Singleton, pays the price on every access. If accessed frequently or accessed during a performance-sensitive section of your code, then it does make sense to avoid f(). Your processor presumably has a finite number of circuits it can devote to branch prediction, and you are being forced to read an atomic variable before the branch anyway. It is a tall price to continually pay for just ensuring that the initialization happened only once.



                      h(), described below, works very much like g() with an extra indirection, but assumes that h_init() gets called exactly once at the beginning of execution. Preferably, you would define a subroutine that gets called as the line of main(); that calls every function like h_init(), with an absolute ordering. Hopefully, these objects do not need to be destructed.



                      Alternatively, if you use GCC, you can annotate h_init() with __attribute__((constructor)). I prefer the explicitness of the static init subroutine though.



                      A * h_global = nullptr;
                      void h_init() h_global = new A ;
                      A const& h() return *h_global;


                      h2() is just like h(), minus the extra indirection:



                      alignas(alignof(A)) char h2_global [sizeof(A)] = ;
                      void h2_init() new (std::begin(h2_global)) A ;
                      A const& h2() return * reinterpret_cast <A const *> (std::cbegin(h2_global));






                      share|improve this answer














                      share|improve this answer



                      share|improve this answer








                      edited Sep 13 at 1:25

























                      answered Sep 12 at 16:04









                      KevinZ

                      753813




                      753813











                      • given that g() accesses global that is constructed before main() is called, I wouldn't worry too much about its thread safety
                        – Dev Null
                        Sep 12 at 20:25











                      • @DevNull There is also the ordering problem: if nontrivial static object x is depended upon to initialize some other static object y in a different translation unit, and y gets initialized first, y may be initialized with a bogus value.
                        – KevinZ
                        Sep 13 at 1:29










                      • true, but this can be worked around with nifty counters.
                        – Dev Null
                        Sep 13 at 2:12










                      • @DevNull Disagree, that trick looks like a disaster waiting to happen. If you already have a specific ordering, you should just make that explicit.
                        – KevinZ
                        Sep 13 at 17:41
















                      • given that g() accesses global that is constructed before main() is called, I wouldn't worry too much about its thread safety
                        – Dev Null
                        Sep 12 at 20:25











                      • @DevNull There is also the ordering problem: if nontrivial static object x is depended upon to initialize some other static object y in a different translation unit, and y gets initialized first, y may be initialized with a bogus value.
                        – KevinZ
                        Sep 13 at 1:29










                      • true, but this can be worked around with nifty counters.
                        – Dev Null
                        Sep 13 at 2:12










                      • @DevNull Disagree, that trick looks like a disaster waiting to happen. If you already have a specific ordering, you should just make that explicit.
                        – KevinZ
                        Sep 13 at 17:41















                      given that g() accesses global that is constructed before main() is called, I wouldn't worry too much about its thread safety
                      – Dev Null
                      Sep 12 at 20:25





                      given that g() accesses global that is constructed before main() is called, I wouldn't worry too much about its thread safety
                      – Dev Null
                      Sep 12 at 20:25













                      @DevNull There is also the ordering problem: if nontrivial static object x is depended upon to initialize some other static object y in a different translation unit, and y gets initialized first, y may be initialized with a bogus value.
                      – KevinZ
                      Sep 13 at 1:29




                      @DevNull There is also the ordering problem: if nontrivial static object x is depended upon to initialize some other static object y in a different translation unit, and y gets initialized first, y may be initialized with a bogus value.
                      – KevinZ
                      Sep 13 at 1:29












                      true, but this can be worked around with nifty counters.
                      – Dev Null
                      Sep 13 at 2:12




                      true, but this can be worked around with nifty counters.
                      – Dev Null
                      Sep 13 at 2:12












                      @DevNull Disagree, that trick looks like a disaster waiting to happen. If you already have a specific ordering, you should just make that explicit.
                      – KevinZ
                      Sep 13 at 17:41




                      @DevNull Disagree, that trick looks like a disaster waiting to happen. If you already have a specific ordering, you should just make that explicit.
                      – KevinZ
                      Sep 13 at 17:41

















                       

                      draft saved


                      draft discarded















































                       


                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function ()
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52198322%2fis-access-to-a-static-function-variable-slower-than-access-to-a-global-variable%23new-answer', 'question_page');

                      );

                      Post as a guest













































































                      這個網誌中的熱門文章

                      Is there any way to eliminate the singular point to solve this integral by hand or by approximations?

                      Why am i infinitely getting the same tweet with the Twitter Search API?

                      Carbon dioxide