Техника оптимизации под линуха

       

Выравнивание переходов


Процессоры, основанные на ядрах Intel P6 и AMD K6, не требуют выравнивания переходов, исключая тот случай, когда целевая инструкция или сама команда перехода расщепляется границей кэш-линейки напополам, в результате чего наблюдается значительное падение производительности. Наибольший ущерб причиняют переходы, находящиеся в циклах с компактным телом и большим уровнем вложения.

Чтобы этого не происходило, некоторые компиляторы прибегают к выравниванию, располагая инструкции перехода по кратным адресам и заполняют образующиеся дыры незначащими инструкциями, такими как XCHG EAX,EAX (обмен содержимого регистров EAX местами) или MOV EAX, EAX (пересылка содержимого EAX в EAX). Это увеличивает размер кода и несколько снижает его производительность, поэтому бездумное выравнивание (оно же "агрессивное") только вредит.

Легко показать, что машинная команда требует выравнивания в тех, и только тех случаях, когда условие ((addr

% cache_len + sizeof(ops)) > cache_len) становится истинным. Здесь: addr – линейный адрес инструкции, cache_len размер кэш-линейки (в зависимости от типа процессора равный 32-, 64- или 128 байтам), ops – сама машинная инструкция. Количество выравнивающих байт рассчитывается по формуле: (cache_len ? (addr % cache_len)).

Именно так поступают все ассеблерщики, но только не компиляторы! MSVC и icl вообще не выравнивают переходов, а gcc выравнивает целевую инструкцию на фиксированную величину, кратную степени двойки (т. е. 2, 4, 8…), что является крайне неэффективной стратегией выравнивания, однако, даже плохая стратегия все же лучше, чем совсем никакой. Кстати говоря, именно поэтому, компиляторы msvc и icl генерируют неустойчивый код, точнее код с "плавающей" производительностью, быстродействие которого главным образом зависит от того, расщепляются ли глубоко вложенные переходы или нет. А это в свою очередь зависит от множества трудно прогнозируемых обстоятельств, включая фазу луны и количество осадков.

Учитывая, что средняя длина x86-инструкций составляет 3.5 байта, целесообразнее всего выравнивать переходы по границе четырех байт (ключ -falign-jumps=4 компилятора gcc). Ключ -fno-align-jumps (или эквивалентный ему -falign-jumps=1) отключает выравнивание. Ключ ?falign-jumps=0

задействует выравнивание по умолчанию, автоматически выбираемое компилятором в зависимости от типа процессора.

if ((addr % cache_len + sizeof(ops)) > cache_len)

                     align = cache_len – (addr % cache_len);



Содержание раздела