与默认IF相比,速记IF是否会提高效率?

时间:2012-12-04 11:44:13

标签: c++ c performance if-statement shorthand

如果我有一个包含任意长度整数的大型数据文件需要按其第二个字段排序:

1 3 4 5
1 4 5 7
-1 34 56 7
124 58394 1384 -1938
1948 3848089 -14850 0
1048 01840 1039 888
//consider this is a LARGE file, the data goes on for quite some time

我呼吁qsort成为我的首选武器,在我的排序功能中,将使用速记IF为数据整理所需的整体时间提供显着的性能提升?或者简写IF仅用作组织代码的便利工具吗?

num2 = atoi(Str);
num1 = atoi(Str2);
LoggNum = (num2 > num1) ? num2 : num1; //faster?

num2 = atoi(Str);
num1 = atoi(Str2);
if(num2 > num1)    //or the same?
    LoggNum = num2;
else
    LoggNum = num1;

5 个答案:

答案 0 :(得分:10)

任何现代编译器都会在这两种情况下构建相同的代码,区别仅在于样式和便利性。

答案 1 :(得分:4)

这个问题没有答案......编译器可以自由生成任何合适的代码。也就是说,只有特别愚蠢的编译器会为这些情况产生明显不同的代码。您应该编写任何您认为最能表达您的程序如何工作的内容...对我而言,三元运算符版本更简洁,更易读,但不熟悉C和/或C ++的人可能会发现if / else版本起初更容易理解。

如果您对此类事情感兴趣并希望更好地了解代码生成,您应该学习如何调用编译器,要求它生成显示程序CPU指令的汇编语言输出。例如,GNU编译器接受生成-S汇编语言文件的.s标志。

答案 2 :(得分:3)

要知道的唯一方法是分析。但是,三元运算符允许您对对象进行初始化:

Sometype z = ( x < y) ? something : somethingElse; // copy initialization

使用if-else,您必须使用额外的分配来获得等效行为。

SomeType z; // default construction
if ( x < y) {
  z = something; // assignment
} else {
  z = somethingElse; // assignment
}

如果分配给SomeType的开销很大,这可能会产生影响。

答案 3 :(得分:3)

我们在VC 2010中测试了这两个陈述:

?=的生成组件是:

 01207AE1 cmp byte ptr [ebp-101h],0
 01207AE8 jne CTestSOFDlg::OnBnClickedButton1+47h (1207AF7h)
 01207AEA push offset (1207BE5h)
 01207AEF call @ILT+840(__RTC_UninitUse) (120134Dh)
 01207AF4 add esp,4
 01207AF7 cmp byte ptr [ebp-0F5h],0
 01207AFE jne CTestSOFDlg::OnBnClickedButton1+5Dh (1207B0Dh)
 01207B00 push offset (1207BE0h)
 01207B05 call @ILT+840(__RTC_UninitUse) (120134Dh)
 01207B0A add esp,4
 01207B0D mov eax,dword ptr [num2]
 01207B10 cmp eax,dword ptr [num1]
 01207B13 jle CTestSOFDlg::OnBnClickedButton1+86h (1207B36h)
 01207B15 cmp byte ptr [ebp-101h],0
 01207B1C jne CTestSOFDlg::OnBnClickedButton1+7Bh (1207B2Bh)
 01207B1E push offset (1207BE5h)
 01207B23 call @ILT+840(__RTC_UninitUse) (120134Dh)
 01207B28 add esp,4
 01207B2B mov ecx,dword ptr [num2]
 01207B2E mov dword ptr [ebp-10Ch],ecx
 01207B34 jmp CTestSOFDlg::OnBnClickedButton1+0A5h (1207B55h)
 01207B36 cmp byte ptr [ebp-0F5h],0
 01207B3D jne CTestSOFDlg::OnBnClickedButton1+9Ch (1207B4Ch)
 01207B3F push offset (1207BE0h)
 01207B44 call @ILT+840(__RTC_UninitUse) (120134Dh)
 01207B49 add esp,4
 01207B4C mov edx,dword ptr [num1]
 01207B4F mov dword ptr [ebp-10Ch],edx
 01207B55 mov eax,dword ptr [ebp-10Ch]
 01207B5B mov dword ptr [LoggNum],eax

和if else运算符:

 01207B5E cmp byte ptr [ebp-101h],0
 01207B65 jne CTestSOFDlg::OnBnClickedButton1+0C4h (1207B74h)
 01207B67 push offset (1207BE5h)
 01207B6C call @ILT+840(__RTC_UninitUse) (120134Dh)
 01207B71 add esp,4
 01207B74 cmp byte ptr [ebp-0F5h],0
 01207B7B jne CTestSOFDlg::OnBnClickedButton1+0DAh (1207B8Ah)
 01207B7D push offset (1207BE0h)
 01207B82 call @ILT+840(__RTC_UninitUse) (120134Dh)
 01207B87 add esp,4
 01207B8A mov eax,dword ptr [num2]
 01207B8D cmp eax,dword ptr [num1]
 01207B90 jle CTestSOFDlg::OnBnClickedButton1+100h (1207BB0h)
 01207B92 cmp byte ptr [ebp-101h],0
 01207B99 jne CTestSOFDlg::OnBnClickedButton1+0F8h (1207BA8h)
 01207B9B push offset (1207BE5h)
 01207BA0 call @ILT+840(__RTC_UninitUse) (120134Dh)
 01207BA5 add esp,4
 01207BA8 mov eax,dword ptr [num2]
 01207BAB mov dword ptr [LoggNum],eax
 01207BB0 cmp byte ptr [ebp-0F5h],0
 01207BB7 jne CTestSOFDlg::OnBnClickedButton1+116h (1207BC6h)
 01207BB9 push offset (1207BE0h)
 01207BBE call @ILT+840(__RTC_UninitUse) (120134Dh)
 01207BC3 add esp,4
 01207BC6 mov eax,dword ptr [num1]
 01207BC9 mov dword ptr [LoggNum],eax

你可以看到?=运算符还有两个asm命令:

01207B4C mov edx,dword ptr [num1]
01207B4F mov dword ptr [ebp-10Ch],edx

需要更多的CPU滴答。

对于一个97000000大小的循环,如果else更快。

答案 4 :(得分:3)

这个特殊的优化让我陷入了真正的代码库,在锁定函数中从一种形式转换到另一种形式,在宏基准测试中节省了10%的执行时间。

让我们在MacOS上用gcc 4.2.1测试一下:

int global;

void
foo(int x, int y)
{
    if (x < y)
        global = x;
    else
        global = y;
}

void
bar(int x, int y)
{
    global = (x < y) ? x : y;
}

我们得到(清理):

_foo:
        cmpl    %esi, %edi
        jge     L2
        movq    _global@GOTPCREL(%rip), %rax
        movl    %edi, (%rax)
        ret
L2:
        movq    _global@GOTPCREL(%rip), %rax
        movl    %esi, (%rax)
        ret

_bar:
        cmpl    %edi, %esi
        cmovle  %esi, %edi
        movq    _global@GOTPCREL(%rip), %rax
        movl    %edi, (%rax)
        ret

考虑到专门添加了cmov指令以提高此类操作的性能(通过避免管道停顿),很明显这个特定编译器中的“?:”更快。

编译器是否应在两种情况下生成相同的代码?是。会吗?当然不是,没有编译器是完美的。如果你真的关心性能(在大多数情况下你不应该),请测试并查看。