我的程序符合严格的别名规则,除了一个地方:一个包含散列函数的编译单元,如MurmurHash3,SpookyHash等。在x86和x86_64上,这些散列函数接受const char *
,投射它们到uint32
,并以4个字节的块处理数据。与逐字节处理数据相比,这使得它们更快,但我相信这打破了严格的别名规则。现在,我用-fno-strict-aliasing编译这个编译单元,而我用-fstrict-aliasing编译程序的其余部分。
但我想知道如果启用链接时优化会发生什么。据我所知,GCC和Clang通过将程序源存储到.o文件中来实现链接时优化排序,以便在链接阶段编译器知道整个程序的源代码。那么仍然可以仅为散列函数禁用严格别名吗?或者我现在必须为整个程序禁用严格别名?或者我是否完全误解了事情,而且MurmurHash3 / SpookyHash实际上是严格的别名合规?
答案 0 :(得分:6)
有三件事需要考虑:
如果您避免复制数据并保证对齐访问,您将获得最佳性能。
未对齐的访问和别名是可移植性问题。如果您决定复制数据,这将同时处理两者。否则,您必须调整算法以处理错误对齐的输入数据,并保证通过不兼容类型的指针不存在竞争访问:
如果您只通过单一指针类型访问数据,违反有效的输入规则将使您的程序不符合要求,但在实践中可能不会成为问题,即使您没有通过-fno-strict-aliasing
- 这是单元测试非常方便的地方。
对于SpookyHash,我实际上有my own C99 version(它还修复了参考实现的V2中的一个一个)。如果您违反有效类型并且您的体系结构支持未对齐访问(或所有输入数据已对齐),您可以传递-DSPOOKY_NOCOPY
编译器标志。在我的x86-64机器上,性能增益大约为10-20%,具体取决于输入大小。
答案 1 :(得分:6)
现在,我用-fno-strict-aliasing编译这个编译单元,而我用-fstrict-aliasing编译程序的其余部分。
您可以使用链接时优化来执行相同的操作。只是不要使用链接时优化来编译特定的目标代码。
clang
示例(与gcc
相同):
clang -flto -O3 -c a.c
clang -O3 -fno-strict-aliasing b.c # no -flto and with -fno-strict-aliasing
clang -flto -O3 -c main.c
clang a.o b.o main.o -o main
答案 2 :(得分:3)
应该可以(至少你可以尝试),因为最近的GCC提供了function specific option pragmas。您可以尝试添加类似
的内容 #pragma GCC optimize ("-fstrict-aliasing")
在别名函数之前,然后输入
#pragma GCC reset_options
在他们之后。
也许你需要用
包装它们#if __GNUC__ >= 4
当然还有一些#endif
或者,使用构建器技巧(例如autoconf
,cmake
或复杂的GNU make 4.0规则等)将您自己的HAVE_GENUINE_GCC
定义为1仅用于真正的GCC编译器,以及您自己的HAVE_GENUINE_CLANG
为1,用于真正的Clang/LLVM编译器等等。或者可能检测到某些示例代码上理解了上述编译指示,然后定义{ {1}}为1。
HAVE_WORKING_PRAGMA_GCC_OPTIMIZE
不在目标文件中存储程序源的表示,但只是某些GCC内部表示的消化形式(如Gimple等) ...)编译源代码时获得。这是完全不同的!
PS。我没试过,所以也许并不那么简单。