静态常量与重复调用函数中的常量

发布于 2024-08-03 04:54:36 字数 270 浏览 4 评论 0原文

我只是想知道

void test()
{
   const static int ABC = 12;
   cout << ABC;
}

有什么不同

void test()
{
   const int ABC = 12;
   cout << ABC;
}

如果在程序执行时重复调用此函数 ?我的意思是,有性能差异吗?或者有什么理由让你更喜欢其中一种而不是另一种?

I'm just wondering how

void test()
{
   const static int ABC = 12;
   cout << ABC;
}

is different from

void test()
{
   const int ABC = 12;
   cout << ABC;
}

if this function is repeatedly called while the program is executing? What I mean is, is there any performance difference? Or is there a reason why you should prefer one over the other?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

独自唱情﹋歌 2024-08-10 04:54:36

有几个因素会影响答案:

  • 首先,只要该值为 const,在任何情况下都几乎肯定会被优化掉。这意味着生成的代码很可能是相同的。
  • 其次,静态成员存储在其他地方,这意味着局部性较小,并且可能会出现缓存未命中的情况。
  • 第三,初始化的成本取决于类型。就您而言,对于 int 来说,初始化的成本基本上不存在。对于更复杂的用户定义类型,它可能会很大。

所以答案是,在足够简单的情况下,编译器可以计算出它们并进行优化,这使得差异为零。在你的例子中几乎肯定是这种情况。

只要变量具有易于构造且成本低廉的类型,请选择非静态以避免缓存未命中。

如果构造该类型的成本很高,您可能希望使用static

当然,最后也是最重要的一点是:

不要相信我们的猜测。如果您担心性能,那么只有一个正确的做法:

  • 对其进行测量,以验证它确实是一个问题
  • 测量每个可能的解决方案的性能
  • 选择能带来最佳性能的解决方案。

There are a couple of things that affect the answer:

  • First, as long as the value is const it will almost certainly be optimized out in any case. That means that the resulting code will most likely be the same.
  • Second, static members are stored elsewhere which means less locality and probably a cache miss.
  • Third, the cost of initialization depends on the type. In your case, for an int, the cost of initialization is basically nonexistent. For more complex user-defined types, it may be huge.

So the answer is, in cases simple enough for the compiler to figure them out and optimize, it makes zero difference. In your example that would almost certainly be the case.

As long as the variable has a type that is easy and cheap to construct, prefer non-static to avoid the cache miss.

If the type is expensive to construct, you might want to use static.

And of course, last, and most importantly of all:

Don't trust our guesswork. If you are concerned about performance, there is only one correct course of action:

  • Measure it, to verify that it is actually a problem
  • Measure the performance of each possible solution
  • Pick the solution that results in the best performance.
今天小雨转甜 2024-08-10 04:54:36

理论上来说,前一种方法要好一点,因为它只实例化变量一次。

然而,编译器将简单地删除“const”行并替换为“cout << 12;”进入函数(显然,当在优化的情况下编译时)。

well theoretically the former method is a tiny bit better as it only insantiates the variable once.

However the compiler will simply remove either "const" line and substitute "cout << 12;" into the function (When compiled with optimisations on, obviously).

请你别敷衍 2024-08-10 04:54:36

在第一次转换中,ABC 将仅在第一次函数调用时初始化一次。在第二种情况下,ABC 每次都会被初始化。如果 ABC 是带有构造函数的复杂类型,您会感觉到不同。它可以分配内存或初始化一些互斥体。对于 int 来说,实践中没有区别。

根据 C++03 标准 3.7.1/2:

如果静态存储持续时间的对象具有初始化或具有副作用的析构函数,则即使它看起来未使用,也不应将其消除,除非可以按照中的规定消除类对象或其副本
12.8。

In first cast ABC will be initialized only once, on first function call. In second case ABC will be initialized every time. You'll feel the difference if ABC is complex type with constructor. It could allocate memory or initialize some mutex. For int there is no difference in practice.

According to C++03 Standard 3.7.1/2:

If an object of static storage duration has initialization or a destructor with side effects, it shall not be eliminated even if it appears to be unused, except that a class object or its copy may be eliminated as specified in
12.8.

浅黛梨妆こ 2024-08-10 04:54:36

这取决于编译器。

在嵌入式软件中,静态常量通常存储在闪存(即代码存储器)中,并且可以像普通变量一样直接访问,而不需要初始化。

相反,非静态 const 可能将其存储在闪存中,但 const 本身将像变量一样在堆栈上创建,并像变量一样进行初始化。

如果这就是您的编译器处理这些情况的方式,那么 static const 会更有效,因为它既不需要堆栈分配也不需要初始化。

显然,非嵌入式编译器可能会以不同的方式处理这些场景。

It depends on the compiler.

In embedded software, a static const will typically be stored in flash (i.e. code memory), and will be accessed directly, like a normal variable, without any need for initialisation.

In contrast, a non-static const may have its value stored in flash, but the const itself will be created on the stack like a variable, and be initialised just like a variable.

If this is how your compiler handles these scenarios, then the static const is more efficient, as it requires neither stack allocation nor initialisation.

Obviously, these scenarios may be handled different by non-embedded compilers.

绝不服输 2024-08-10 04:54:36

您还可以通过使用编译器的汇编器或类似以下在线实用程序来很好地了解编译器将执行的操作:https:// /godbolt.org 并查看生成的汇编代码。

这值得一看,因为这个问题很根本。

基于您的问题的示例,考虑本地与静态类型声明和实例化,查看基元与“复杂”(甚至不是非常):

#include <iostream>

struct non_primitive
{
    int v;
    non_primitive(const int v_) : v(v_) {}
};

void test_non_static_const_primitive()
{
   const int ABC = 12;
   std::cout << ABC;
}

void test_static_const_primitive()
{
   const static int ABC = 12;
   std::cout << ABC;
}

void test_non_static_constant_non_primitive_global_struct() 
{
    const non_primitive s(12);
    std::cout << s.v;
}

void test_non_static_constant_non_primitive_local_struct() 
{
    struct local_non_primitive
    {
        int v;
        local_non_primitive(const int v_) : v(v_) {}
    };
    const local_non_primitive s(12);
    std::cout << s.v;
}

void test_static_constant_non_primitive_global_struct() 
{
    const static non_primitive s(12);
    std::cout << s.v;
}

void test_static_constant_non_primitive_local_struct() 
{
    struct local_non_primitive
    {
        int v;
        local_non_primitive(const int v_) : v(v_) {}
    };
    const static local_non_primitive s(12);
    std::cout << s.v;
}

编译器认识到它不必处理本地 const 复杂类型的任何内存链接,并且前四种情况的处理方式相同。看起来编译器并没有尝试探索复杂类型的相对简单性以及静态实例化中进一步简化的潜力。

由此产生的成本差异是比较巨大的。

使用gcc7.2和-O3优化编译的汇编代码:

test_non_static_const_primitive():
  mov esi, 12
  mov edi, OFFSET FLAT:std::cout
  jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
test_static_const_primitive():
  mov esi, 12
  mov edi, OFFSET FLAT:std::cout
  jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
test_non_static_constant_non_primitive_global_struct():
  mov esi, 12
  mov edi, OFFSET FLAT:std::cout
  jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
test_non_static_constant_non_primitive_local_struct():
  mov esi, 12
  mov edi, OFFSET FLAT:std::cout
  jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
test_static_constant_non_primitive_global_struct():
  movzx eax, BYTE PTR guard variable for test_static_constant_non_primitive_global_struct()::s[rip]
  test al, al
  je .L7
  mov esi, DWORD PTR test_static_constant_non_primitive_global_struct()::s[rip]
  mov edi, OFFSET FLAT:std::cout
  jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
.L7:
  sub rsp, 8
  mov edi, OFFSET FLAT:guard variable for test_static_constant_non_primitive_global_struct()::s
  call __cxa_guard_acquire
  test eax, eax
  mov esi, DWORD PTR test_static_constant_non_primitive_global_struct()::s[rip]
  je .L8
  mov edi, OFFSET FLAT:guard variable for test_static_constant_non_primitive_global_struct()::s
  mov DWORD PTR test_static_constant_non_primitive_global_struct()::s[rip], 12
  call __cxa_guard_release
  mov esi, 12
.L8:
  mov edi, OFFSET FLAT:std::cout
  add rsp, 8
  jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
test_static_constant_non_primitive_local_struct():
  movzx eax, BYTE PTR guard variable for test_static_constant_non_primitive_local_struct()::s[rip]
  test al, al
  je .L14
  mov esi, DWORD PTR test_static_constant_non_primitive_local_struct()::s[rip]
  mov edi, OFFSET FLAT:std::cout
  jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
.L14:
  sub rsp, 8
  mov edi, OFFSET FLAT:guard variable for test_static_constant_non_primitive_local_struct()::s
  call __cxa_guard_acquire
  test eax, eax
  mov esi, DWORD PTR test_static_constant_non_primitive_local_struct()::s[rip]
  je .L15
  mov edi, OFFSET FLAT:guard variable for test_static_constant_non_primitive_local_struct()::s
  mov DWORD PTR test_static_constant_non_primitive_local_struct()::s[rip], 12
  call __cxa_guard_release
  mov esi, 12
.L15:
  mov edi, OFFSET FLAT:std::cout
  add rsp, 8
  jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
_GLOBAL__sub_I__Z31test_non_static_const_primitivev:
  sub rsp, 8
  mov edi, OFFSET FLAT:std::__ioinit
  call std::ios_base::Init::Init()
  mov edx, OFFSET FLAT:__dso_handle
  mov esi, OFFSET FLAT:std::__ioinit
  mov edi, OFFSET FLAT:std::ios_base::Init::~Init()
  add rsp, 8
  jmp __cxa_atexit

You can also get a good idea of what the compiler will do by making use of your compiler's assembler or something like this online utility: https://godbolt.org and taking a look at the resulting assembly code.

This was worth a look, since the question is fundamental.

An example based on your question, looking at primitives vs "complex" (not even very) considering local vs static type declaration and instantiation:

#include <iostream>

struct non_primitive
{
    int v;
    non_primitive(const int v_) : v(v_) {}
};

void test_non_static_const_primitive()
{
   const int ABC = 12;
   std::cout << ABC;
}

void test_static_const_primitive()
{
   const static int ABC = 12;
   std::cout << ABC;
}

void test_non_static_constant_non_primitive_global_struct() 
{
    const non_primitive s(12);
    std::cout << s.v;
}

void test_non_static_constant_non_primitive_local_struct() 
{
    struct local_non_primitive
    {
        int v;
        local_non_primitive(const int v_) : v(v_) {}
    };
    const local_non_primitive s(12);
    std::cout << s.v;
}

void test_static_constant_non_primitive_global_struct() 
{
    const static non_primitive s(12);
    std::cout << s.v;
}

void test_static_constant_non_primitive_local_struct() 
{
    struct local_non_primitive
    {
        int v;
        local_non_primitive(const int v_) : v(v_) {}
    };
    const static local_non_primitive s(12);
    std::cout << s.v;
}

The compiler recognizes that it doesn't have to deal with any memory linkage for local const complex types, and the first four cases are treated identically. It looks like the compiler doesn't try to explore the complex type's relative simplicity and potential for further simplification in the static instantiation.

The resulting cost difference is relatively huge.

The compiled assembly code using gcc7.2 with -O3 optimization:

test_non_static_const_primitive():
  mov esi, 12
  mov edi, OFFSET FLAT:std::cout
  jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
test_static_const_primitive():
  mov esi, 12
  mov edi, OFFSET FLAT:std::cout
  jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
test_non_static_constant_non_primitive_global_struct():
  mov esi, 12
  mov edi, OFFSET FLAT:std::cout
  jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
test_non_static_constant_non_primitive_local_struct():
  mov esi, 12
  mov edi, OFFSET FLAT:std::cout
  jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
test_static_constant_non_primitive_global_struct():
  movzx eax, BYTE PTR guard variable for test_static_constant_non_primitive_global_struct()::s[rip]
  test al, al
  je .L7
  mov esi, DWORD PTR test_static_constant_non_primitive_global_struct()::s[rip]
  mov edi, OFFSET FLAT:std::cout
  jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
.L7:
  sub rsp, 8
  mov edi, OFFSET FLAT:guard variable for test_static_constant_non_primitive_global_struct()::s
  call __cxa_guard_acquire
  test eax, eax
  mov esi, DWORD PTR test_static_constant_non_primitive_global_struct()::s[rip]
  je .L8
  mov edi, OFFSET FLAT:guard variable for test_static_constant_non_primitive_global_struct()::s
  mov DWORD PTR test_static_constant_non_primitive_global_struct()::s[rip], 12
  call __cxa_guard_release
  mov esi, 12
.L8:
  mov edi, OFFSET FLAT:std::cout
  add rsp, 8
  jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
test_static_constant_non_primitive_local_struct():
  movzx eax, BYTE PTR guard variable for test_static_constant_non_primitive_local_struct()::s[rip]
  test al, al
  je .L14
  mov esi, DWORD PTR test_static_constant_non_primitive_local_struct()::s[rip]
  mov edi, OFFSET FLAT:std::cout
  jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
.L14:
  sub rsp, 8
  mov edi, OFFSET FLAT:guard variable for test_static_constant_non_primitive_local_struct()::s
  call __cxa_guard_acquire
  test eax, eax
  mov esi, DWORD PTR test_static_constant_non_primitive_local_struct()::s[rip]
  je .L15
  mov edi, OFFSET FLAT:guard variable for test_static_constant_non_primitive_local_struct()::s
  mov DWORD PTR test_static_constant_non_primitive_local_struct()::s[rip], 12
  call __cxa_guard_release
  mov esi, 12
.L15:
  mov edi, OFFSET FLAT:std::cout
  add rsp, 8
  jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
_GLOBAL__sub_I__Z31test_non_static_const_primitivev:
  sub rsp, 8
  mov edi, OFFSET FLAT:std::__ioinit
  call std::ios_base::Init::Init()
  mov edx, OFFSET FLAT:__dso_handle
  mov esi, OFFSET FLAT:std::__ioinit
  mov edi, OFFSET FLAT:std::ios_base::Init::~Init()
  add rsp, 8
  jmp __cxa_atexit
情独悲 2024-08-10 04:54:36

对于基本类型,例如整数值,那么我会将静态的使用作为“过早的优化”,除非您已经做了一些基准测试并考虑了各种权衡(例如将静态初始化为非静态)零值通常需要表中的一个条目来指定加载代码时要设置的位置、大小和初始值)。

除非您使用指向 int 的指针并且在函数返回后取消引用它,否则您不需要 static - 让编译器进行优化。

如果在函数退出后取消引用指向该值的指针,那么我会将其归类为持久状态变量,并且最好在类或模块级别定义它以明确这一点。

For a basic type, such as an integer value, then I would pigeon hole the use of static as a "premature optimisation" unless you have done some benchmarking and taken into account the various trade offs (for example initialising a static to a non-zero value often requires an entry in a table to specify the position, size and initial value to be set when the code is loaded).

Unless you are taking a pointer to the int and it is dereferenced after your function returns then you don't need the static - let the compiler do the optimisation.

If a pointer to the value is dereferenced after your function has exited then I would class it as a persistent state variable and it would be good practice to define it at at the class or module level to make that clear.

烟若柳尘 2024-08-10 04:54:36

我会选择第二个——它更具可读性。为什么不必要地添加一个关键字(静态),它实际上不会给阅读代码的人增加任何价值。

I would go with the second one - it is more readable. why unnecesarily add a keyword (static) that does not really add any value to someone reading the code.

痴者 2024-08-10 04:54:36

static const 示例当然可以节省后续调用的执行时间,对于使用 static const 的复杂对象构造来说,速度要快得多,但我质疑是否需要将 ABC 限制在函数范围内,并引入从第一次调用到后续调用的函数行为变化来电。通常一个文件包含耦合的功能耦合函数,只需给出 ABC 文件范围并使用它即可。

The static const example certainly saves on execution time for subsequent calls, much faster for complex object construction to use static const, but I question the need to limit the ABC to function scope and introduce a variation in behaviour on the function from 1st call to following calls. Usually a file contains coupled functionally coupled functions, just give ABC file scope and be done with it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文