Ускорение ветвления в C++

Немного поигрался с довольно редко испольуземой при разработке приложений дерективой компилятора __builtin_expect. Эта директива поддерживатеся как Clang, так и в GCC, а в случае с MSVC есть довольно похожая директива __assume, работающая только для switch, и которую, если верить MSDN, использовать не рекомендутся, так как она ведет к потенциальным проблемам. Сказать насколько может быть полезна директива __builtin_expect сложно, но мало ли, к примеру товарищу на моем любимом форуме это зачем-то понадобилось.
Немного о самой __builtin_expect. При генерировании ассемблерных инструкций, компилятор самостоятельно решает о том, какая из ветвей условия должна быть размещена в начале, а к какой необходимо перейти посредством условного перехода. Данное поведение никак не регламентируется стандартом и полностью зависит не только от компилятора, но и от выбранного уровня оптимизации, в чем можно легко убедится получив несколько ассемблерных листингов одного и того же кода. При помощи же __builtin_expect можно указать компилятору какую из ветвей в условии if-else или switch-case разместить первой, а на какую организовать условный переход.
Для тестов пришлось немного нагородить кода, так как при включеной оптимизации компилятор выкидывает все с его точки зрения лишнее. В данном примере директева __builtin_expect говорит компилятору о том, что в начале надо разместить код условия else, а только потом if.

if(__builtin_expect(selector > 0, 0))
    foo1(1);
else
    foo2(2);

И соответствующий кусок ассемблерного кода.

    test    EDI, EDI
    jg  LBB2_1
## BB#2:                                ## %if.else
    mov EDI, 2
    pop RBP
    jmp __Z4foo2i               ## TAILCALL
LBB2_1:                                 ## %if.then
    mov EDI, 1
    pop RBP
    jmp __Z4foo1i               ## TAILCALL

Лично я помню только один случай и своего опыта, когда подобное поведение бы мне пригодилось. Тогда я писал перекодировку межде графическими форматами и у меня почти всегда выполнялось определенное условие, причем проверка вызывалась очень часто.

2 Comments Ускорение ветвления в C++

  1. Bulat Ziganshin

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

    вообще есть книги Агнера Фога о микроархитектруре разных x86 – советую почитать, хотя бы ради интеллектуального удовольствия

    Reply

Leave a Reply