3

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

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;
4

5 回答 5

10

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

于 2012-12-04T11:46:05.937 回答
4

这个问题没有答案……编译器可以自由地生成它认为合适的任何代码。也就是说,只有一个特别愚蠢的编译器才会为这些情况生成截然不同的代码。你应该写任何你觉得最能表达你的程序如何工作的东西......对我来说,三元运算符版本更简洁易读,但不习惯 C 和/或 C++ 的人可能会发现 if/else 版本一开始更容易理解。

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

于 2012-12-04T11:47:08.003 回答
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
}

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

于 2012-12-04T11:48:04.530 回答
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 滴答声。

如果 else 更快,则为 97000000 大小的循环。

于 2012-12-04T12:02:50.673 回答
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 指令来提高这种操作的性能(通过避免管道停顿),很明显,这个特定编译器中的“?:”更快。

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

于 2012-12-04T12:31:57.433 回答