C++ 中的编译器指令重新排序优化(以及阻碍它们的因素)

2024-04-30

我已将代码缩减为以下内容,这在保留我感兴趣的编译器输出的同时,尽可能简单。

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
}

将 VC10 与 /O2 和 /Ob1 一起使用,生成的程序集几乎反映了上述 C++ 代码中的指令顺序。由于本地数组ar仅传递给bar()当条件失败并且未使用时,我希望编译器优化为如下所示。

if((used & some_global_array[0]) != 0)
{
    return;
}

// Now do the copying to ar and call bar(ar)...

编译器不这样做是因为在一般情况下很难识别此类优化吗?或者它是否遵循一些严格的规则来禁止这样做?如果是这样,为什么?有什么方法可以给它一个提示,表明这样做不会改变我的程序的语义?

注意:显然,通过重新排列代码来获得优化输出是微不足道的,但我感兴趣why在这种情况下编译器不会优化,不会how在这种(有意简化的)情况下这样做。


可能这没有得到优化的原因是全局数组。编译器无法事先知道是否访问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};

第二个非常令人费解,但这可能只是一个错过的优化(或者也许我错过了其他东西)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ 中的编译器指令重新排序优化(以及阻碍它们的因素) 的相关文章

随机推荐