【问题标题】:Will replacing 'extern const' with 'static const' affect performance?将 'extern const' 替换为 'static const' 会影响性能吗?
【发布时间】:2015-09-10 01:32:09
【问题描述】:

我们的 C 代码库跨两个文件存储所有全局常量:

//global.h
extern const long double ACCELERATION_GRAVITY_FTS;
extern const long double PI;
extern const long double DEG_TO_RAD;
extern const long double RAD_TO_DEG;
extern const long double GC_NM_PER_RAD;
extern const long double FEET_PER_NM;
...

//global.c
const long double ACCELERATION_GRAVITY_FTS = 32.17405;
const long double PI = 3.1415926535897932384626433832795;
const long double DEG_TO_RAD = 0.01745329251994329576923690768489;
const long double RAD_TO_DEG = 57.295779513082320876798154814105;
const long double GC_NM_PER_RAD = 3437.74677471314;
const long double FEET_PER_NM = 6076.1155;

为避免重复,我想将它们重构为一个文件:

//global.h
static const long double ACCELERATION_GRAVITY_FTS = 32.17405;
static const long double PI = 3.1415926535897932384626433832795;
static const long double DEG_TO_RAD = 0.01745329251994329576923690768489;
static const long double RAD_TO_DEG = 57.295779513082320876798154814105;
static const long double GC_NM_PER_RAD = 3437.74677471314;
static const long double FEET_PER_NM = 6076.1155;
//global.c no longer exists

虽然这在可维护性方面显然是一个很好的重构,
对性能和可执行文件大小实用吗?

【问题讨论】:

  • static const 方式为编译器提供了用常量表达式替换变量引用的机会。
  • 在 C 中,这是一个非常糟糕的主意。这些不是常量,而是常量变量。 IE。它们像变量一样被访问,const 只是程序员不写它们的保证。问题是它们不会被优化,例如作为即时负载。 C方式是将#define它们作为宏,然后使它们成为真正的常量。
  • @CoffeeandCode:我不会在 C 中打赌。这是 C 和 C++ 之间的区别之一。
  • @TrevorHickey:为了确保,您应该尝试一下。 C 不保证这一点;但是,编译器可能会优化,但仅针对 static,而不是跨 CU。
  • @Olaf,好的。不管这种优化是否发生,你都说相反的情况很可能发生。如果使用静态,同一个变量最终可能会多次存储在二进制文件中(因为它存在于包含它的每个翻译单元中)?

标签: c performance static global extern


【解决方案1】:

除非volatile 或由函数初始化,否则static const 变量将是编译时常量。 (无论如何,使用任何体面的优化编译器)

因此,如果您确实使用 static const 变量,则可以提高速度更小的二进制文件。

一个例子是:

extern volatile const int n;

int main(){
    volatile int i = n;
}

volatile const int n = 5;

其中有 x86 程序集:

main:
    mov eax, DWORD PTR n[rip]
    mov DWORD PTR [rsp-4], eax
    xor eax, eax
    ret
n:
    .long   5

我们必须使用volatile 来强制编译器获取变量的地址(如果变量不是volatile 而是在另一个翻译单元中,就会发生这种情况)并且不优化int i

static const 相同的示例:

static const int n = 5;

int main(){
    volatile int i = n;
}

具有 x86 程序集:

main:
    mov DWORD PTR [rsp-4], 5
    xor eax, eax
    ret

我们不必在常量上使用volatile,因为我们会以与使用标头完全相同的方式公开变量,但我们仍然需要停止编译器优化i

您可以看到static const 方式少了一条指令,extern 方式在二进制文件中添加了额外的数据,以供参考。

因此我们获得了更好的性能更小的二进制文件。虽然,我承认,这些例子都很琐碎。

这实际上也不是一个完美的表示。如果我们在另一个翻译单元中定义了const int n,没有链接时优化,编译器将无法输出n: .long 5,并且必须引用另一个变量。但是我们会给出这个例子的疑问。

这些优化非常常见,您基本上可以依赖它。

唯一需要注意的是,如果你写这样的东西:

static const int n = some_func();

int main(){
    volatile int i = n;
}

编译器不能用n 代替它的字面值。这会给您的二进制文件增加膨胀,因为您在标题中定义它,并且它将在每个翻译单元中重新声明一次。所以extern 在这种情况下对空间来说会更好,但可能不是速度;你可以自己测试一下。如果您确实需要进行微优化,只需混合搭配即可。

[所有示例均使用来自https://gcc.godbolt.org/ 的 gcc 4.9.2 编译并使用标志 -O3]

【讨论】:

    【解决方案2】:

    您的建议是有效的代码,它可能提高您的运行速度并且可能增加您的可执行文件的大小。

    在这两种情况下,这取决于您的编译器在优化方面的能力。

    从概念上讲,static const 版本意味着每个翻译单元都有自己的常量副本。

    但这也意味着有更大的优化能力,因为编译器可以看到常量的值;它可以将值直接包含在正在使用的任何计算中,而不是从链接的内存位置检索值。

    【讨论】:

    • 你的意思是说“可能会缩小尺寸”吗?
    • @TrevorHickey 没有。它可能会增加大小,因为它使每个单元都有一个常量副本,而最初整个程序只有一个副本。
    • 好的,有道理。在某些示例中,它可以减少程序大小,但正如您在此处所述,它也有可能增加程序大小。
    • 是的,这完全取决于编译器。
    【解决方案3】:

    不,我认为这不会损害性能或可执行文件大小。 (相反,它可能会提高性能。)

    可能有正当理由更喜欢extern。 例如:您的 const long double 值被编译到共享库中。对于使用该库的任何程序,这些 const 变量可以在您更改编译库时相应更改。也就是说,只有在要更改共享库的值时才重新编译共享库。相反,如果在这种情况下使用单个标头,则需要重新编译所有程序以使其更新。

    【讨论】:

    • 是的,您永远不知道何时需要更改 π 的值。 :-)
    • @rici 例如,精度
    • @rici 另一个例子,你可能想在开发游戏时使用一个假的 π
    • @Griddoor:嗯?什么是假π?共享库在哪里? OP 只声明它将在两个模块中。我认为这是针对特定项目的。
    • @Olaf 我的意思是33.1 或任何适合游戏设计的数字。有时游戏中的测量与我们世界中的物理和数学不同。是的,OP 没有提到共享库或特定项目。仅提及代码库。所以我讨论了我能想到的。
    猜你喜欢
    • 1970-01-01
    • 2019-07-23
    • 1970-01-01
    • 2019-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-18
    • 2011-11-09
    相关资源
    最近更新 更多