visual编译器指令在C++中重新排序优化(以及禁止它们的原因)
visual Compiler instruction reordering optimizations in C++ (and what inhibits them)
我已经将代码简化为以下内容,这是我所能做到的最简单的内容,同时保留了我感兴趣的编译器输出。
void foo(const uint64_t used)
{
uint64_t ar[100];
for(int i = 0; i < 100; ++i)
{
ar[i] = some_global_array[i];
}
const uint64_t mask = ar[0];
if((used & mask) != 0)
{
return;
}
bar(ar); // Not inlined
}
使用带有/O2和/Ob1的VC10,生成的程序集在很大程度上反映了上述C++代码中的指令顺序。由于本地数组ar
只有在条件失败时才传递给bar()
,并且在其他情况下是未使用的,所以我希望编译器优化到以下内容。
if((used & some_global_array[0]) != 0)
{
return;
}
// Now do the copying to ar and call bar(ar)...
编译器之所以不这样做,是因为它在一般情况下很难识别这样的优化吗?还是它遵循了一些严格的规则,禁止它这样做?如果是这样的话,为什么?我有没有办法给它一个提示,这样做不会改变我的程序的语义?
注意:很明显,只需重新排列代码就可以获得优化的输出,但我感兴趣的是为什么编译器在这种情况下不会优化,而不是在这种(有意简化的)情况下如何优化。
这可能是全局数组没有得到优化的原因。编译器事先无法知道,例如,访问some_global_array[99]
是否会导致生成某种异常/信号,因此必须执行整个循环。如果全局数组是在同一个编译单元中静态定义的,情况会大不相同。
例如,在LLVM中,全局数组的以下三个定义将产生该函数截然不同的输出:
// this yields pretty much what you're seeing
uint64_t *some_global_array;
// this calls memcpy and then performs the conditional check
uint64_t some_global_array[100] = {0};
// this calls memset (not memcpy!) on the ar array and then bar directly (no
// conditional checks since the array is const and filled with 0s, so the if
// is always false)
const uint64_t some_global_array[100] = {0};
第二个非常令人费解,但它可能只是错过了优化(或者我错过了其他东西)。
没有"严格的规则"来控制编译器可以输出什么样的汇编语言。如果编译器可以确定,由于某些先决条件,某个代码块不需要执行(因为它没有副作用),那么它绝对允许短路整个代码块。
在一般情况下,这种优化可能相当复杂,您的编译器可能不会花那么多精力。如果这是性能关键代码,那么您可以(按照您的建议)微调源代码,以帮助编译器生成最佳的汇编代码。不过,这是一个反复尝试的过程,对于下一版本的编译器,您可能需要再次尝试。
相关文章:
- 空基优化子对象的地址
- 关闭||运算符优化
- 如何解决gcc编译器优化导致的centos双编译器设置中的分段错误
- 返回值优化:显式移动还是隐式
- 人脸跟踪arduino代码的优化
- 使用仅使用一次的变量调用的复制构造函数.这可能是通过调用move构造函数进行编译器优化的情况吗
- 纯函数,为什么没有优化
- 为什么大多数 pair 实现默认不使用压缩(空基优化)?
- 如何以优化的方式同时迭代两个间距不相等的数组
- 小字符串优化(调试与发布模式)
- 浮点定向舍入和优化
- Visual Studio 调试优化如何工作?
- ISO C++禁止声明没有类型的"setInputNombre"
- 禁止显示由于常量为零而比较始终为假的警告
- 为什么开关的优化方式与 c/c++ 中的链接不同?
- 线性优化目标函数中的绝对值
- 当空基类也是成员变量时,为什么禁止空基优化?
- clang实现char8_t的方式是否存在缺陷,或者标准的某个黑暗角落是否禁止优化?
- visual编译器指令在C++中重新排序优化(以及禁止它们的原因)
- 如何在使用全局变量作为参数时禁止c++编译器优化