将 'extern const' 替换为 'static const' 会影响性能吗?
Will replacing 'extern const' with 'static const' affect performance?
我们的 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
虽然这在可维护性方面显然是一个很好的重构,
它对性能和可执行文件大小是否实用?
不,我认为这不会影响性能或可执行文件的大小。 (相反,它可能会提高性能。)
可能有充分的理由更喜欢 extern
。
例如:您的 const long double
值被编译到共享库中。对于使用该库的任何程序,当您更改已编译的库时,这些 const 变量会相应地更改。也就是说你只有在re-compile共享库的时候才想改变它们的值。相比之下,如果在这种情况下使用单个 header,则需要 re-compile 所有程序才能使它们更新。
您的建议是有效代码,它可能提高您的运行速度并且可能增加您的可执行文件的大小。
在这两种情况下,这取决于您的编译器在优化方面的表现。
从概念上讲,static const
版本意味着每个翻译单元都有自己的常量副本。
但这也意味着有更强的优化能力,因为编译器可以看到常量的值;它可以直接将值包含在正在使用的任何计算中,而不是从链接的内存位置检索值。
A static const
变量将是一个 compile-time 常量,除非 volatile
或由函数初始化。
(无论如何,使用任何像样的优化编译器)
因此,如果您使用 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
,因为我们将以与使用 header 完全相同的方式公开变量,但我们仍然需要停止编译器优化 i
.
可以看到static const
方式少了一条指令,extern
方式在二进制中添加了额外的数据,用于需要存储以供参考的文字。
所以我们得到更好的性能和一个更小的二进制文件。虽然,我承认,这些例子非常微不足道。
这实际上也不是一个完美的代表。如果我们在另一个翻译单元中定义了 const int n
,没有 link-time 优化,编译器将无法输出 n: .long 5
并且必须引用另一个变量。但是我们会给出这个例子的怀疑。
这些优化非常普遍,您基本上可以相信它是可用的。
唯一要注意的是,如果你这样写:
static const int n = some_func();
int main(){
volatile int i = n;
}
编译器无法用 n
替换它的字面值。这会给你的二进制文件增加膨胀,因为你在 header 中定义它,并且每个翻译单元都会 re-declared 一次。所以在那种情况下 extern
对 space 会更好,但速度可能不快;你可以自己测试一下。如果您确实需要 micro-optimize.
,只需混搭即可
[所有示例均使用来自 https://gcc.godbolt.org/ 的 gcc 4.9.2 编译并使用标志 -O3]
我们的 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
虽然这在可维护性方面显然是一个很好的重构,
它对性能和可执行文件大小是否实用?
不,我认为这不会影响性能或可执行文件的大小。 (相反,它可能会提高性能。)
可能有充分的理由更喜欢 extern
。
例如:您的 const long double
值被编译到共享库中。对于使用该库的任何程序,当您更改已编译的库时,这些 const 变量会相应地更改。也就是说你只有在re-compile共享库的时候才想改变它们的值。相比之下,如果在这种情况下使用单个 header,则需要 re-compile 所有程序才能使它们更新。
您的建议是有效代码,它可能提高您的运行速度并且可能增加您的可执行文件的大小。
在这两种情况下,这取决于您的编译器在优化方面的表现。
从概念上讲,static const
版本意味着每个翻译单元都有自己的常量副本。
但这也意味着有更强的优化能力,因为编译器可以看到常量的值;它可以直接将值包含在正在使用的任何计算中,而不是从链接的内存位置检索值。
A static const
变量将是一个 compile-time 常量,除非 volatile
或由函数初始化。
(无论如何,使用任何像样的优化编译器)
因此,如果您使用 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
,因为我们将以与使用 header 完全相同的方式公开变量,但我们仍然需要停止编译器优化 i
.
可以看到static const
方式少了一条指令,extern
方式在二进制中添加了额外的数据,用于需要存储以供参考的文字。
所以我们得到更好的性能和一个更小的二进制文件。虽然,我承认,这些例子非常微不足道。
这实际上也不是一个完美的代表。如果我们在另一个翻译单元中定义了 const int n
,没有 link-time 优化,编译器将无法输出 n: .long 5
并且必须引用另一个变量。但是我们会给出这个例子的怀疑。
这些优化非常普遍,您基本上可以相信它是可用的。
唯一要注意的是,如果你这样写:
static const int n = some_func();
int main(){
volatile int i = n;
}
编译器无法用 n
替换它的字面值。这会给你的二进制文件增加膨胀,因为你在 header 中定义它,并且每个翻译单元都会 re-declared 一次。所以在那种情况下 extern
对 space 会更好,但速度可能不快;你可以自己测试一下。如果您确实需要 micro-optimize.
[所有示例均使用来自 https://gcc.godbolt.org/ 的 gcc 4.9.2 编译并使用标志 -O3]