繁体   English   中英

编译器指令重新排序C ++中的优化(以及禁止它们的原因)

[英]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
}

将VC10与/ O2和/ Ob1一起使用,生成的程序集几乎反映了上述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};

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

没有“严格的规则”来控制允许编译器输出什么样的汇编语言。 如果编译器可以确定由于某些先决条件而不需要执行代码块(因为它没有副作用),那么绝对允许将整个事件短路。

在一般情况下,这种优化可能相当复杂,并且您的编译器可能不会全力以赴。 如果这是性能关键代码,那么您可以微调源代码(如您所建议的那样)以帮助编译器生成最佳汇编代码。 这是一个试错过程,您可能必须再次为下一版本的编译器执行此操作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM