2015-02-14 2 views
-5

Какой из ниже код будет более оптимизирован для эффективности первой функции или второй функции в C/C++ компилятор GCC?если-иначе, если лестница и Compiler Оптимизация

// First Function 
if (A && B && C) { 
    UpdateData(); 
} else if (A && B){ 
    ResetData(); 
} 

//Second Function 
if (A && B) { 
    if (C) { 
     UpdateData(); 
    } else { 
     ResetData(); 
    } 
} 
  1. ли мы получить какое-либо улучшение производительности в Второй функции?
  2. Если Используется первая функция, может ли компилятор ее оптимизировать до Второй метод сам по себе?
+6

Вы можете посмотреть на скомпилированный код, но моя ставка будет будь они компилируются в один и тот же код. Это также пахнет микро-оптимизацией, просто напишите, что наиболее читаемо. – CoryKramer

+0

... если A или B не связаны с оценкой, которая может иметь побочные эффекты. – nneonneo

+1

Почему это помечено C? C не имеет методов. – m0skit0

ответ

2

Большая часть этого вопроса будет зависеть от того, что A, B и C на самом деле (и компилятор будет оптимизировать его, как показано ниже). Простые типы, определенно не стоит беспокоиться. Если это какие-то «объекты с большим количеством чисел» или какой-то сложный тип данных, для которого требуется 1000 инструкций для каждого, «это true или нет», тогда будет большая разница, если компилятор решит сделать другой код.

Как всегда, когда дело доходит до производительности: Измерьте в своем собственном коде, используйте профилирование, чтобы определить, где код тратит БОЛЬШЕ времени, а затем измерять с изменениями этого кода. Повторяйте до тех пор, пока он не запустится достаточно быстро [независимо от того, что] и/или ваш менеджер говорит вам прекратить возиться с кодом. Как правило, однако, если он не является ДЕЙСТВИТЕЛЬНО высокой областью трафика кода, не будет иметь большого значения переупорядочить условия в if-statement, это общий алгоритм, который в наибольшей степени влияет на общий случай.

Если мы предположим, A, B и C являются простыми типами, такими как int, мы можем написать код для исследования:

extern int A, B, C; 
extern void UpdateData(); 
extern void ResetData(); 

void func1() 
{ 
    if (A && B && C) { 
     UpdateData(); 
    } else if (A && B){ 
     ResetData(); 
    } 
} 


void func2() 
{ 
    if (A && B) { 
     if (C) { 
      UpdateData(); 
     } else { 
      ResetData(); 
     } 
    } 
} 

GCC 4.8.2 с учетом этого, с -O1 производит этот код:

_Z5func1v: 
    cmpl $0, A(%rip) 
    je .L6 
    cmpl $0, B(%rip) 
    je .L6 
    subq $8, %rsp 
    cmpl $0, C(%rip) 
    je .L3 
    call _Z10UpdateDatav 
    jmp .L1 
.L3: 
    call _Z9ResetDatav 
.L1: 
    addq $8, %rsp 
.L6: 
    rep ret 

_Z5func2v: 
.LFB1: 
    cmpl $0, A(%rip) 
    je .L12 
    cmpl $0, B(%rip) 
    je .L12 
    subq $8, %rsp 
    cmpl $0, C(%rip) 
    je .L9 
    call _Z10UpdateDatav 
    jmp .L7 
.L9: 
    call _Z9ResetDatav 
.L7: 
    addq $8, %rsp 
.L12: 
    rep ret 

другими словами: никакой разницы

Использование лязг ++ 3.7 (как о е около 3 недель назад) с -O1 дает это:

_Z5func1v:        # @_Z5func1v 
    cmpl $0, A(%rip) 
    setne %cl 
    cmpl $0, B(%rip) 
    setne %al 
    andb %cl, %al 
    movzbl %al, %ecx 
    cmpl $1, %ecx 
    jne .LBB0_2 
    movl C(%rip), %ecx 
    testl %ecx, %ecx 
    je .LBB0_2 
    jmp _Z10UpdateDatav   # TAILCALL 
.LBB0_2:        # %if.else 
    testb %al, %al 
    je .LBB0_3 
    jmp _Z9ResetDatav   # TAILCALL 
.LBB0_3:        # %if.end8 
    retq 

_Z5func2v:        # @_Z5func2v 
    cmpl $0, A(%rip) 
    je .LBB1_4 
    movl B(%rip), %eax 
    testl %eax, %eax 
    je .LBB1_4 
    cmpl $0, C(%rip) 
    je .LBB1_3 
    jmp _Z10UpdateDatav   # TAILCALL 
.LBB1_4:        # %if.end4 
    retq 
.LBB1_3:        # %if.else 
    jmp _Z9ResetDatav   # TAILCALL 
.Ltmp1: 

цепное и в func1 от лязга может быть полезным, но это, вероятно, такая небольшая разница, что вы должны сосредоточиться на том, что имеет смысл от логическая перспектива кода.

В заключение: Не стоит

Higher оптимизация в г ++ делает это не сделать то же оптимизацию tailcall, что лязг делает, иначе никакой разницы.

Однако, если мы делаем A, B и C во внешние функции, которые компилятор не может «понять», то мы получим разницу:

_Z5func1v:        # @_Z5func1v 
    pushq %rax 
.Ltmp0: 
    .cfi_def_cfa_offset 16 
    callq _Z1Av 
    testl %eax, %eax 
    je .LBB0_3 

    callq _Z1Bv 
    testl %eax, %eax 
    je .LBB0_3 

    callq _Z1Cv 
    testl %eax, %eax 
    je .LBB0_3 

    popq %rax 
    jmp _Z10UpdateDatav   # TAILCALL 
.LBB0_3:        # %if.else 
    callq _Z1Av 
    testl %eax, %eax 
    je .LBB0_5 

    callq _Z1Bv 
    testl %eax, %eax 
    je .LBB0_5 

    popq %rax 
    jmp _Z9ResetDatav   # TAILCALL 
.LBB0_5:        # %if.end12 
    popq %rax 
    retq 

_Z5func2v:        # @_Z5func2v 
    pushq %rax 
.Ltmp2: 
    .cfi_def_cfa_offset 16 
    callq _Z1Av 
    testl %eax, %eax 
    je .LBB1_4 

    callq _Z1Bv 
    testl %eax, %eax 
    je .LBB1_4 

    callq _Z1Cv 
    testl %eax, %eax 
    je .LBB1_3 

    popq %rax 
    jmp _Z10UpdateDatav   # TAILCALL 
.LBB1_4:        # %if.end6 
    popq %rax 
    retq 
.LBB1_3:        # %if.else 
    popq %rax 
    jmp _Z9ResetDatav   # TAILCALL 

Здесь мы видим разницу между func1 и func2, где func1 будет звонить A и B дважды - поскольку компилятор не может предположить, что вызов этих функций ONCE будет делать то же самое, что и вызов дважды. [Учтите, что функции A и B могут считывать данные из файла, вызывая rand или что-то еще, результат НЕ НАЗЫВАЕТ эту функцию, так как программа ведет себя по-разному.

(В данном случае я только разместил код лязг, но г ++ производит код, который имеет тот же результат, но несколько иной порядок различных кусков кода)

Смежные вопросы