Question

If i have a large data file containing integers of an arbitrary length needing to be sorted by it's second field:

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

and i call upon qsort to be my weapon of choice, inside my sort function, will using the shorthand IF provide a significant performance boost to overall time it takes the data to be sorted? Or is the shorthand IF only used as a convenience tool for organizing code?

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;
Était-ce utile?

La solution

Any modern compiler will build identical code in these two cases, the difference is one of style and convenience only.

Autres conseils

There is no answer to this question... the compiler is free to generate whatever code it feels suitable. That said, only a particularly stupid compiler would produce significantly different code for these cases. You should write whatever you feel best expresses how your program works... to me the ternary operator version is more concise and readable, but people not used to C and/or C++ may find the if/else version easier to understand at first.

If you're interested in things like this and want to get a better feel for code generation, you should learn how to invoke your compiler asking it to produce assembly language output showing the CPU instructions for the program. For example, GNU compilers accept a -S flag that produces .s assembly language files.

The only way to know is to profile. However, the ternary operator allows you to do an initialization of an object:

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

With if-else, you would have to use an extra assignment to get the equivalent behaviour.

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

This could have an impact if the overhead of assigning to a SomeType is large.

We tested these two statements in VC 2010:

The geneated assembly for ?= is:

 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

and for if else operator:

 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

as you can see ?= operator has two more asm commands:

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

which takes more CPU ticks.

for a loop with 97000000 size if else is faster.

This particular optimization has bitten me in a real code base where changing from one form to the other in a locking function saved 10% execution time in a macro benchmark.

Let's test this with gcc 4.2.1 on MacOS:

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

We get (cleaned up):

_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

Considering that the cmov instructions were added specifically to improve the performance of this kind of operation (by avoiding pipeline stalls), it is clear that "?:" in this particular compiler is faster.

Should the compiler generate the same code in both cases? Yes. Will it? No of course not, no compiler is perfect. If you really care about performance (in most cases you shouldn't), test and see.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top