Инлайн асмовый совершенно бессмысленно здесь. GCC уже знает, как оптимизировать абсолютную ценность, и скрытие этого процесса от компилятора внутри встроенного asm сделает ваш код более оптимистичным, а не лучше. https://gcc.gnu.org/wiki/DontUseInlineAsm
Дать абсолютное значение в чистых C всегда, по крайней мере так же хорошо (если компилятор не решает сделать код ветвистого после встраивания во что-то, и профилировании показывает, что разветвление было неправильным выбором.)
absval(int x) {
return x<0 ? -x : x; // ternary often compiles branchlessly
}
К преимуществам над inline asm относятся: компиляторы знает Результат неотрицателен и может оптимизироваться соответствующим образом. Например, он может делить на 2 с простым сдвигом вправо, вместо того, чтобы учитывать различное округление сдвигов против.С подписано разделение:
void foo_asm (int *arr, int len) {
for (int i=0 ; i<1024 ; i++){
arr[i] = optAbsVal(arr[i])/4; // Using Ross's correct implementation
}
}
внутренний цикл (from gcc6.3 -O3 -mcpu=cortex-a7 -mthumb
on the Godbolt compiler explorer):
.L4:
ldr r3, [r2, #4]
CMP r3, #0 @@@@ Inline asm version
IT LT
RSBLT r3, r3, #0
adds r1, r3, #3
bics r3, r3, r3, asr #32
it cs
movcs r3, r1 @ x = x<0 ? x+3 : x (I think, I didn't look up BICS)
asrs r3, r3, #2 @ x >>= 2
str r3, [r2, #4]!
cmp r2, r0
bne .L4
против
void foo_pure (int *arr, int len) {
for (int i=0 ; i<1024 ; i++){
arr[i] = absval(arr[i])/4; // Using my pure C
}
}
.L8: @@@@@@@@ Pure C version
ldr r3, [r2, #4]
cmp r3, #0 @ gcc emitted exactly your 3-insn sequence on its own
it lt
rsblt r3, r3, #0
asrs r3, r3, #2 @ non-negative division by 4 is a trivial >> 2
str r3, [r2, #4]!
cmp r1, r2
bne .L8
Зная, что подписанный переменная является неотрицательным часто является ценным для компилятора. (И signed overflow is undefined behaviour, так что это позволено игнорировать тот факт, что 0 - 0x80000000
= 0x80000000
, т.е. -INT_MIN
все еще имеет свой знак установлен бит, потому что -INT_MIN
является UB. most negative number является частным случаем для дополнения до 2.)
gcc мог бы сделать еще лучше, посмотрев флаги, уже установленные предыдущими инструкциями, вместо того, чтобы делать cmp
. (Это также может обеспечить лучшее планирование инструкций для ячеек в порядке).
Но absval(100 + arr[i])
я вижу
adds r3, r3, #100
cmp r3, #0
it lt
rsblt r3, r3, #0
вместо использования the sign flag alone for the MInus condition.
@ hand-written, IDK why gcc doesn't do this, probably missed optimization:
adds r3, r3, #100 # set flags
it MI # use the MInus condition instead of LessThan
rsbmi r3, r3, #0
Инлайн ASM также не воспользоваться инструкциями 3-операнда руки. rsb
может выдавать результат в другом регистре, чем вход (в режиме ARM, по крайней мере, и в унифицированном синтаксисе IT
не требует режима большого пальца). Но вы не можете просто использовать отдельный выходной операнд для x
, если вы хотите, чтобы ваш asm все еще собирался в режиме Thumb, где rsb r1, r0, #0
не собирался.
А также встроенные asm блокируют постоянное распространение. optAbsVal(-1)
компилирует 4 инструкции, чтобы перевернуть их во время выполнения. absval(-1)
компилируется до постоянной времени компиляции 1
.
На объектах с NEON, inline-asm также нельзя авто-векторизовать. Он также может заставить компилятор не развернуть цикл, если он в противном случае имел бы.
Проверьте свой результат с помощью objdump, поскольку asm не является изменчивым, может быть, компилятор удаляет его вместе. Важно проверить двоичный код, сгенерированный при выполнении таких функций. – auselen
@auselen GCC автоматически обрабатывает встроенные операторы сборки без выходных операндов как изменчивые, иначе он всегда будет устранять их при включении оптимизации. Здесь не проблема. –
Обратите внимание, что у вас есть функция, помеченная 'inline'; когда копия функции _body_ встроена на сайт вызова, больше нет _call_, поэтому, как только вы завершаете оптимизатор, все ставки не учитываются при распределении регистров, независимо от того, какое соглашение может видеть внешняя видимая копия функции , – Notlikethat