Существует множество архитектур, которые могут выполнять такие операции в одной инструкции. Например, a*2 + b
компилируется в
lea eax, [rsi+rdi*2]
on x86-64
add r0, r1, r0, lsl #1
на ARM
add w0, w1, w0, lsl 1
на ARM64
lda16 r0, r1[r0]
на xcore
Компилятор соответствующим образом оптимизирует выражение. Нет причин делать такие вещи, как a *= 2; a += b
, что во многих случаях снижает удобочитаемость.
Вы можете увидеть демо на Компилятор Проводник
Однако если вы спросите об этом только потому, что выполняете эту операцию миллиарды раз, то это, по сути, проблема XY, потому что изменение версии C - неправильный способ, а сокращение количества инструкций - это не то, как вы сокращаете время выполнения. Вы не измеряете производительность по количеству инструкций
Современные процессоры суперскалярны, а некоторые инструкции микрокодированы, поэтому одна сложная инструкция может быть медленнее, чем несколько простых инструкций, которые могут выполняться параллельно. Компиляторы, очевидно, знают это и будут учитывать задержку при компиляции. Реальное решение - использовать многопоточность и SIMD.
Например, Clang выдает следующие инструкции в основном цикле для AVX-512
vpaddd zmm0, zmm0, zmm0 ; a *= 2
vpaddd zmm1, zmm1, zmm1
vpaddd zmm2, zmm2, zmm2
vpaddd zmm3, zmm3, zmm3
vpaddd zmm0, zmm0, zmmword ptr [rsi + 4*rdx] ; a += b
vpaddd zmm1, zmm1, zmmword ptr [rsi + 4*rdx + 64]
vpaddd zmm2, zmm2, zmmword ptr [rsi + 4*rdx + 128]
vpaddd zmm3, zmm3, zmmword ptr [rsi + 4*rdx + 192]
который включает как разворачивание цикла, так и автоматическая векторизация. Каждая инструкция может работать одновременно с шестнадцатью 32-битными целыми числами. Конечно, если вы используете 64-битную int
, тогда она может работать "только" с 8 за раз. Кроме того, каждая из тех же инструкций может выполняться независимо от других, поэтому, если у ЦП достаточно портов выполнения, он может добавить 64 int
параллельно. Вот что мы называем "быстрым"
GCC часто менее агрессивен при развертывании цикла и использует vpslld
, за которым следует vpaddd
. Но это все равно быстрее, чем скалярная версия. На ARM с неоном видно, что используется shl v0.4s, v0.4s, 1; add v0.4s, v0.4s, v1.4s
. Вот Compiler Проводник демо ссылка
В сочетании с многопоточностью это намного быстрее, чем ваша "оптимизация"
person
phuclv
schedule
05.04.2019
lea
может делатьa*2+b
, еслиb
между 0 и 4096, или он у вас в реестре. - person ugoren   schedule 11.02.2012