我有一个包含许多小内联函数的头文件。他们中的大多数碰巧有不断的数据。由于这些函数对性能至关重要,因此它们处理常量的方式变得非常重要。 AFAIK有两种方法可以引用常量:
1)将它们定义在稍后与应用程序链接的单独源文件中。
2)就地定义常数。
我会选择后一种方式,因为它更易于维护。但是,如果编译器没有优化内联创建的数千个相等常量,那么它可能会更慢。
问题:
编译器会结合这些相等的常量吗?特别是,将使用以下哪种方法?
1)在编译单元中组合相等的常量
2)在链接模块(整个程序或库)中组合相等的常量
3)将常量与任何静态常量数据相结合,这些数据碰巧具有相同的位模式,并满足编译单元或整个程序的对齐要求。
我使用现代编译器(GCC4.5)。
我不是汇编程序方面的专家,因此我无法使用几个简单的测试来回答这个问题:)
修改
常量非常大(大多数至少为16个字节),因此编译器无法使它们成为立即值。
EDIT2:
代码示例
这个就地使用常数:
float_4 sign(float_4 a)
{
const __attribute__((aligned(16))) float mask[4] = { //I use a macro for this line
0x80000000, 0x80000000, 0x80000000, 0x80000000};
const int128 mask = load(mask);
return b_and(a, mask);
}
答案 0 :(得分:8)
根据GCC,以下选项可以满足您的需求:
-fmerge常数
尝试跨编译单元合并相同的常量(字符串常量和浮点常量)。 如果汇编器和链接器支持,则此选项是优化编译的缺省选项。使用-fno-merge-constants可以抑制此行为 在-O,-O2,-O3,-Os等级启用。
答案 1 :(得分:2)
如果您在头文件中定义常量,如下所示:
int const TEN = 10;
// or
enum { ELEVEN = 11 };
也就是说,编译转换单元(.cc源文件)时,编译器不仅可以看到常量声明,而且定义也是可见的,那么编译器当然会用生成的代码中的常量值替换它,即使是没有启用优化。
[max@truth test]$ cat test.cc
int const TEN = 10; // definition available
extern int const TWELVE; // only declaration
int foo(int x) { return x + TEN; }
int bar(int x) { return x + TWELVE; }
[max@truth test]$ g++ -S -o - test.cc | c++filt | egrep -v " *\."
foo(int):
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
movl -4(%rbp), %eax
addl $10, %eax
leave
ret
bar(int):
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
movl TWELVE(%rip), %eax
addl -4(%rbp), %eax
leave
ret
TEN:
注意foo(int)
如何将addl $10, %eax
加法,即TEN常数替换为其值。另一方面,在bar(int)
中,它首先movl TWELVE(%rip), %eax
将TWELVE的值从内存加载到eax寄存器(地址将由链接器解析)然后执行加法{{1} }。
优化版本如下所示:
addl -4(%rbp), %eax
答案 2 :(得分:0)
我认为你的问题没有一般性答案。我只给一个C,C ++的规则是不同的。
这很大程度上取决于常量的类型。一个重要的类是“整数常量表达式”。这些可以在编译时确定,特别是用作“整数枚举常量”的值。无论何时使用
enum { myFavoriteDimension = 55/2 };
对于这样的常数,通常应该发生最好的事情:它们被实现为汇编程序。它们甚至没有存储位置,直接写入汇编程序,你的问题甚至没有意义。
对于其他数据类型,问题更加微妙。尽量强制不要使用“const限定变量”的地址。这可以使用register
关键字完成。
register double const something = 5.7;
可能与上述效果相同。
对于撰写类型(struct
,union
,数组),没有一般的答案或方法。我已经看到gcc能够完全优化小数组(大约10个元素)。