简体   繁体   English

用可变值初始化静态变量有哪些运行时间成本?

[英]What run-time costs are there to initializing a static with a variable value?

In C++, what is the expected runtime cost in a reasonable compiler of initializing a static variable with a variable value as opposed to a constant value? 在C ++中,用合理的编译器初始化带有变量值而不是常量值的静态变量的预期运行时间是多少?

For example consider this code: 例如,考虑以下代码:

bool foo();
bool baz1() {
  const bool value = foo();
  static bool alternate1 = value;
  static bool alternate2 = false;

  // Do something.
  return alternate1;
}

What is the expected run-time cost difference between alternate1 and alternate2 ? alternate1alternate2之间的预期运行时成本差异是多少?

Initialisation from a compile-time constant (alternate 2) will most likely happen during program startup, with no cost each time the function is called. 从编译时常量(替代2)进行初始化很可能会在程序启动期间发生,每次调用该函数都不会产生成本。

Thread-safe initialisation of a local static variable will cause the compiler to generate something like this pseudocode: 局部静态变量的线程安全初始化将导致编译器生成类似以下伪代码的内容:

static bool alternate1;

static bool __initialised = false;
static __lock_type __lock;
if (!__initialised) {
    acquire(__lock);
    if (!__initialised) {
        alternate1 = value;
        __initialised = true;
    }
    release(__lock);
}

So there is likely to be a test of a flag each time the function is called (perhaps involving a memory barrier or other synchronisation primitive), and a further cost of acquiring and releasing a lock the first time. 因此,每次调用该函数(可能涉及内存屏障或其他同步原语)时,都有可能对标志进行测试,并且首次获取和释放锁会产生进一步的开销。

Note that in your code, foo() is called every time, whether or not the variable is initialised yet. 请注意,在代码中,无论变量是否已初始化,每次都会调用foo() It would only be called the first time, if you changed the initialisation to 如果您将初始化更改为

static bool alternate1 = foo();

The details are implementation-dependent of course; 当然,细节取决于实现。 this is based on my observations of the code produced by GCC. 这是基于我对GCC生成的代码的观察。

static variables are initialized at the start of your program, which means the init will only occur once. static变量在程序开始时进行初始化,这意味着初始化只会发生一次。 The cost for just a boolean is very low, and for your alternate1 will be the cost of executing foo() , which in your example is not much, since it's just an empty function. 仅一个布尔值的成本就非常低,而对于alternate1将是执行foo()的成本,在您的示例中这并不算多,因为它只是一个空函数。

To generalize, the cost will be the maximum cost of either initializing your basic type (int, float, etc) or the cost of initializing (running the ctor) of any user defined type/library defined type. 概括地说,成本将是初始化基本类型(int,float等)或任何用户定义类型/库定义类型的初始化(运行ctor)的最大成本。 Any static that is initialized with a function, then the max will be the cost of the function being executed. 用函数初始化的任何static ,则最大值将是函数执行的成本。

It appears that your question is not about a static variable in general, but rather about a static variable declared inside a function. 看来,您的问题通常与静态变量无关,而与在函数内部声明的静态变量有关。

The additional run-time costs of initializing such variable from a run-time value stem from several sources 从运行时值初始化此类变量的额外运行时成本来自多个来源

  1. Such variable should be initialized only once, when the control passes over its declaration the very first time (if ever). 当控件第一次超过其声明时(如果有的话),此类变量应仅初始化一次。 In order to achieve that an additional boolean variable/flag is allocated for each such variable and checked every time the control passes over the declaration. 为了实现为每个此类变量分配一个附加的布尔变量/标志,并在每次控件通过声明时进行检查。 If the flag says that the variable is not initialized yet, it gets initialized. 如果标志指示变量尚未初始化,则将其初始化。

  2. For those static variables has non-trivial destructors, the language has to guarantee that their destruction order at program termination is the reverse of their construction order. 对于那些具有非平凡析构函数的静态变量,该语言必须保证在程序终止时其销毁顺序与它们的构造顺序相反。 Since the construction order determined at run-time, the program has to prepare a run-time structure to schedule the future destructions. 由于施工顺序是在运行时确定的,因此程序必须准备一个运行时结构以计划将来的破坏。 This is also done as part of step 1 for variables with non-trivial destructors: they are registered in a "list" of constructed objects in order of their construction. 对于具有非平凡析构函数的变量,这也作为步骤1的一部分完成:按构造顺序将它们注册在构造对象的“列表”中。 The "list" is typically implemented as a pre-allocated array (since its maximum size is known at compile time). “列表”通常实现为预分配的数组(因为在编译时已知其最大大小)。

  3. In multithreaded configurations the above steps might/will be accompanied with additional locking/unlocking steps. 在多线程配置中,上述步骤可能会/将伴随其他锁定/解锁步骤。

Since these are all "household" expenses implemented "under the hood", the actual cost might greatly depend on the implementation. 由于这些都是在“幕后”实施的“家庭”支出,因此实际成本可能在很大程度上取决于实施。 See/profile the code your specific compiler generates. 查看/分析您的特定编译器生成的代码。

Setting alternate1 value will mean calling the function, and even if the function is returning a static value, you will have to save the stack, call the function, get its return value, restore the stack and assign the value to a variable. 设置alternate1值将意味着调用该函数,即使该函数返回静态值,也将必须保存堆栈,调用该函数,获取其返回值,还原堆栈并将该值分配给变量。

Literally, in the first case you will be executing at least 8 assembly code lines, and in the second one – only one line. 从字面上看,在第一种情况下,您将执行至少8条汇编代码行,而在第二种情况下,将仅执行一行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM