2012-02-25 123 views
3

我已经减少了我的代码下到下面,这很简单,只要我能把它同时保留了编译器输出我感兴趣。编译器指令重新排序用C++优化(什么阻碍他们)

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)... 

是编译器不这样做,因为它只是太难为它在一般情况下,识别此类优化?还是遵循一些严格的规定,禁止它这样做?如果是这样,为什么,有什么办法可以给我一个暗示,这样做不会改变我的程序的语义?

注:显然这将是微不足道的只是重新安排代码,即可获得最优化的输出,但我在很感兴趣,为什么编译器不会在这样的情况下优化,不如何在这个这么做(故意简化)的情况。

回答

1

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

这种类型的优化,可在一般的情况相当复杂,和你的编译器可能不会去所有的努力。如果这是性能严重的代码,那么您可以微调源代码(如您所建议的)以帮助编译器生成最好的汇编代码。尽管这是一个反复试验的过程,但您可能需要再次为下一版本的编译器执行此操作。

+0

我认为“严格规定”不好措辞,我想我真的问的是,因为它被允许为所欲为,只要它能够确定它是不会改变程序逻辑,有没有一些具体的事情在这种情况下,防止它假设没有分配给本地阵列的副作用,哪一个控制路径刚丢弃而不使用?如果答案仅仅是在一般情况下很困难,那就够公平的了,这对我来说只是令人惊讶,因为这似乎是最容易检测的事情之一。 – kamrann 2012-02-25 09:13:39

+0

我能想到的唯一的事情是编译器无法知道是否会与其他线程发生可能的交互,尤其是与全局数组。 – 2012-02-25 09:25:16

+0

因此,一个更简化的案例仍然没有在任何主要编译器上得到优化,[根据LLVM家伙](http://lists.cs.uiuc.edu/pipermail/llvmdev/2012-February/047750 .html)在一般情况下,就像你所说的那样,这太简单了。 – kamrann 2012-02-27 23:33:37

3

也许这是为什么没有得到最优化的原因是全球阵列。如果访问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}; 

第二是相当令人费解,但它可以简单地是一个错过的优化(或也许我错过了别的东西)。

+0

对,我也认为全球可能是问题所在,但仍然和你一样,我很惊讶没有第二种情况的优化,这是我如何定义全局的。实际上VC10仍然复制,然后检查条件,即使是一个常量全局。无论如何,我进一步简化为一个foo()接受3个额外的常量整数参数a,b和c,把它们放到一个大小为3的局部数组中,完全抛弃全局,然后测试used&a。它在测试条件之前仍然分配了b&c。有任何想法吗? – kamrann 2012-02-25 09:21:50

+0

更改编译器。 ;-) – CAFxX 2012-02-25 10:35:29

+0

把笑话放在一边:http://lists.cs.uiuc.edu/pipermail/llvmdev/2012-February/047746.html – CAFxX 2012-02-25 11:18:50