Рубрика: интересные компиляторы



Поговорим просто про сложение. На x86 сложение имеет 2 операнда addq %reg1, %reg2, что означает reg2 += reg1. На ARM сложение имеет 3 операнда add x3, x2, x1, что означает x3 = x1 + x2.



Когда вы складываете несколько чисел подряд (скажем, для простоты 4 числа x1 = x1 + x2 + x3 + x4), то это делается в 3 инструкции



x5 = x1 + x2

x6 = x3 + x4

x1 = x5 + x6





Такие оптимизации называются tree reduction, чтобы первые две операции исполнялись параллельно в процессоре, так как у них нет зависимости. В итоге такие операции занимают 2 цикла вместо 3.



К сожалению, так как в x86 сложение принимает только 2 операнда, так сделать не получится, либо надо складывать числа как x1 += x2, x3, x4 (цепочка из 3), либо складывать x3 += x4, что не всегда хочется или можно (скажем, менять x2, x3, x4 не хочется). Есть инструкция lea, но на x86 не всегда хватает регистров, чтобы сделать это быстро, поэтому в целом tree reduction не очень применяется.



Так вот, так как clang слишком сильно и годами оптимизировался Долиной под x86, такие сложения редко оптимизировались в целом и оставались просто через add.



И да, clang как-то слишком топорно оптимизирует сложения 4 чисел на Arm, где у нас 16 регистров вообще



        add     x13, x13, x9

add x13, x13, x10

add x13, x13, x12





То есть цепочка из 3, когда увидел, прям ощутились оптимизации, на которые забили, когда смотрели на код x86. И такие вещи забавно прослеживаются, когда смотришь декомпиляцию clang под Arm -- много оптимизаций или их отсутствие как на платформе x86.



GCC, кстати получше это делает



        add     x1, x4, x1

add x6, x3, x2

add x1, x6, x1





Интересная заключительная мораль в том, что мы даже сложения чисел не можем адекватно соптимизировать в 2022. Ну бывает, что ж



Поиграться: https://gcc.godbolt.org/z/1nozoz1M4