2013-09-01 6 views
7

Рассмотрим следующую программу C:статического предсказания ветвлений/оптимизации GCC

void bar(); 
void baz(); 

void foo(int a) { 
    if (a) { 
     bar(); 
    } 
    else { 
     baz(); 
    } 
} 

На моем x86-64 на основе компьютера, инструкции, генерируемые НКУ с уровнем оптимизации -O1 дает:

0: sub $0x8,%rsp 
4: test %edi,%edi 
6: je  14 <foo+0x14> 
8: mov $0x0,%eax 
d: callq 12 <foo+0x12> # relocation to bar 
12: jmp 1e <foo+0x1e> 
14: mov $0x0,%eax 
19: callq 1e <foo+0x1e> # relocation to baz 
1e: add $0x8,%rsp 
22: retq 

, тогда как добавление параметра оптимизации -freorder-блоков (входит в -O2) превращает код в:

0: sub $0x8,%rsp 
4: test %edi,%edi 
6: jne 17 <foo+0x17> 
8: mov $0x0,%eax 
d: callq 12 <foo+0x12> # relocation to baz 
12: add $0x8,%rsp 
16: retq 
17: mov $0x0,%eax 
1c: callq 21 <foo+0x21> # relocation to bar 
21: add $0x8,%rsp 
25: retq 

что в основном меняется от прыжок равен до прыжок не равен. Я знаю, что до Pentium 4 предсказание статической ветви на условной передовой ветви считалось не принятым процессором (кажется, что статические предсказания стали случайными на последующих процессорах Intel), поэтому я полагаю, что эта оптимизация имеет дело с этим.

Предполагая, что и отсылая к JNE оптимизированной версии, это будет означать, что блок еще является на самом деле считается, что более вероятно, чем выполнена если блока в процессе выполнения программы.

Но что это значит? Поскольку нет значения для значения a в функции компилятора foo, такая вероятность зависит только от писаний программиста (кто мог бы использовать if (!a) вместо if (a) и вызовы с перевернутыми функциями).

Означает ли это, что следует рассматривать как эффективную практику лечения , если условные блоки как исключительные случаи (а не нормальный поток выполнения)?

То есть:

if (!cond) { 
    // exceptional code 
} 
else { 
    // normal continuation 
} 

вместо:

if (cond) { 
    // normal continuation 
} 
else { 
    // exceptional code 
} 

(конечно, можно было бы предпочесть использовать оператор возврата внутри соответствующего блока, чтобы ограничить размер отступа).

ответ

4

У меня когда-то было значительное количество действий по оптимизации производительности на ARM (7,9). Это был простой C, достаточно тупой компилятор (SDT AFAIR). Одним из способов сохранения ресурсов ЦП был анализ if ветвей и переписывание if, поэтому нормальный поток не прерывает последовательность линейных инструкций. Это имело положительный эффект как из-за блока прогнозирования процессора, так и для более эффективного использования и более эффективного использования кеш-памяти сегмента кода.

Я думаю, здесь мы видим оптимизацию, которая очень близка. В первом фрагменте кода обе ветви приводят к нарушению нормальной последовательности (строка с лавелем 6 для одной ветви и 12 для другого). Во втором фрагменте одна команда ветвления упорядочена до retq, а другая последовательность ветвей имеет один прыжок (не хуже, чем в первом фрагменте). Обратите внимание на инструкции 2 retq.

Так как я могу видеть, что это не вопрос je или jne, но скорее вопрос блоков переупорядочения так ветвей линейна инструкции последовательность с одним из них вошли без jump и полного предсказания мощности блока сохранен.

Что касается «почему GCC предпочитает одну ветку над другой» ... Я вижу в документации это может быть результат предсказания статической ветви (на основе вызовов внутри единицы перевода?). Во всяком случае, я бы рекомендовал сыграть с __builtin_expect, чтобы получить более подробный ответ.

+0

Да ... Но разные точки возврата могли быть реализованы только в версии 'je'. И GCC делает это, если/else блокирует переупорядочение _consciously_: в исходной программе изменение 'if (a)' to 'if (! A)' компилируется точно противоположно: от 'jne' (не оптимизированной) версии до 'je' (оптимизированная по порядку) версия. Я не могу поверить, что GCC делает это только для того, чтобы высмеять меня! :) – lledr

+0

Я думаю, что в любом случае __builtin_expect должен помочь этому ;-). http://blog.man7.org/2012/10/how-much-do-builtinexpect-likely-and.html –

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