Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 解释GCC编译器优化';什么是不利的绩效影响?_C++_Performance_Gcc_Compiler Optimization - Fatal编程技术网

C++ 解释GCC编译器优化';什么是不利的绩效影响?

C++ 解释GCC编译器优化';什么是不利的绩效影响?,c++,performance,gcc,compiler-optimization,C++,Performance,Gcc,Compiler Optimization,请注意:这个问题既不是关于代码质量和改进代码的方法,也不是关于运行时差异的重要性。这是关于GCC以及为什么哪种编译器优化会影响性能 节目 以下代码统计最多为m的斐波那契素数: intmain(){ 无符号整数m=500000000u; 无符号整数i=0u; 无符号整数a=1u; 无符号整数b=1u; 无符号整数c=1u; 无符号整数计数=0u; 而(a+b我认为这是由编译器在使用-O1和-O2时生成的“条件移动”指令(CMOVEcc)造成的 使用-O0时,如果(c==0u)语句被编译为跳转:

请注意:这个问题既不是关于代码质量和改进代码的方法,也不是关于运行时差异的重要性。这是关于GCC以及为什么哪种编译器优化会影响性能

节目 以下代码统计最多为
m
的斐波那契素数:

intmain(){
无符号整数m=500000000u;
无符号整数i=0u;
无符号整数a=1u;
无符号整数b=1u;
无符号整数c=1u;
无符号整数计数=0u;

而(a+b我认为这是由编译器在使用
-O1
-O2
时生成的“条件移动”指令(CMOVEcc)造成的

使用
-O0
时,如果(c==0u)
语句被编译为跳转:

    cmp     DWORD PTR [rbp-16], 0
    jne     .L4
使用
-O1
-O2

    test    edx, edx
    cmove   ecx, esi
-O3
产生跳转(类似于
-O0
):

其中“使用条件移动而不是比较和分支会导致代码速度几乎降低2倍”


正如rodrigo在他的评论中所建议的那样,如果转换使用标志-fno告诉gcc不要用条件移动替换分支,因此防止了这个性能问题。

我认为问题在于
-fif转换
,它指示编译器执行
CMOV
,而不是
TEST/JZ和
CMOV
在一般情况下不是很好

据我所知,反汇编中有两点受此标志的影响:

首先,将第13行中的if(c==0u){i=a+b;}
编译为:

test   edx,edx //edx is c
cmove  ecx,esi //esi is (a + b), ecx is i
其次,如果(c!=0u){count=count+1u;}编译为

cmp    eax,0x1   //eax is c
sbb    r8d,0xffffffff //r8d is count, but what???
很好的技巧!它将
-1
减去
计数
,但使用进位,并且只有
c
小于
1
时才设置进位,无符号意味着
0
。因此,如果
eax
为0,它将-1减去
计数
,然后再次减去1:它不会改变。如果
eax
不改变0,然后减去
-1
,从而增加变量


现在,这避免了分支,但代价是错过了明显的优化,如果
c==0u
,您可以在迭代时直接跳到下一个
。这一步非常简单,甚至可以在
-O0
中完成。这里重要的事情是循环携带的数据依赖关系

看看最里面循环的慢速变体的机器代码。我在这里展示了
-O2
程序集,
-O1
的优化程度较低,但总体上具有类似的数据依赖性:

.L4:
        xorl    %edx, %edx
        movl    %esi, %eax
        divl    %ecx
        testl   %edx, %edx
        cmove   %esi, %ecx
        addl    $1, %ecx
        cmpl    %ecx, %esi
        ja      .L4
查看
%ecx
中循环计数器的增量如何取决于上一条指令(cmov
),这又取决于除法结果,而除法结果又取决于循环计数器的上一个值

实际上,在计算跨越整个循环的
%ecx
中的值时存在一个数据依赖链,由于执行循环的时间占主导地位,因此计算该链的时间决定了程序的执行时间

调整程序以计算分割数表明它执行434044698
div
指令。在我的例子中,将程序所占用的机器周期数除以该数字得到26个周期,这与
div
指令的延迟加上另一条指令的大约3或4个周期非常接近链中的离子(链为
div
-
test
-
cmov
-
add

相比之下,
-O3
代码没有这种依赖链,因此它是吞吐量范围而不是延迟范围:执行
-O3
变量的时间由计算434044698独立
div
指令的时间决定

最后,为您的问题提供具体答案:

1.哪种优化能够/能够解释性能下降?

正如另一个答案所提到的,这是如果转换创建了一个循环携带的数据依赖项,而最初有一个控制依赖项。当控制依赖项对应于不可预测的分支时,它们可能也很昂贵,但在这种情况下,分支很容易预测

<>>2。有可能解释什么触发了C++代码的优化(即没有对GCC内部结构的深刻理解)?<强> < /P> 也许您可以想象优化将代码转换为

(i=2u;i{ c=(a+b)%i; i=(c!=0)?i:a+b; } 其中,在CPU上对三值运算符进行求值,从而在计算出
c
之前,不知道
i
的新值

3.类似地,对于为什么备选方案(额外观察)明显阻止恶意优化,是否有一个高层次的解释?


在这些变体中,代码不符合if转换的条件,因此没有引入有问题的数据依赖关系。

我认为这可能与
I
中可能存在的溢出有关。请注意,当您想要结束循环时,您执行
I=a+b;
但是增量发生在循环条件之前。如果
a呢+b==MAX_UINT
?如果将
++i
移动到
else
或即使将
i
控制变量添加到循环中,也会恢复丢失的性能。如果转换测试.c返回良好性能,则使用
gcc-O1-fno编译。不知道为什么…检查两个opti的程序集输出正如你们所看到的,ons优化版本生成的CPU指令更少,而这些指令对于当前任务更为优化
.L4:
        xorl    %edx, %edx
        movl    %esi, %eax
        divl    %ecx
        testl   %edx, %edx
        cmove   %esi, %ecx
        addl    $1, %ecx
        cmpl    %ecx, %esi
        ja      .L4