Вы можете сообщить о снижении производительности на баг-трекере clang, особенно если вы можете создать MCVE / [mcve], который медленнее с GCC9 по той же причине, что и ваш основной код.
Обычно вы не можете форсироватьварианты выбора инструкций компилятора, полностью не делая это самостоятельно с встроенным asm.
Но параметры настройки могут иметь значение , например, -march=native
для настройки вашего оборудования. (-march=haswell
или -march=native
на имеет значение, например устанавливает -mtune=haswell
, а также включает все имеющиеся у вас расширения ISA.)
например, gcc или clang иногда избегаютinc
в целом, но используйте его при компиляции для конкретного процессора, у которого нет проблем с ним. (Не говоря о версии lock
, просто inc reg
).
Похоже, что так и есть: использование -march=skylake
приводит к clang9.0 для использования lock inc
вместо lock add [mem], 1
, но не с clang8.0 на Godbolt. (С просто -O3
, clang9.0 по-прежнему использует lock add
/ sub)
inc reg
хорошо для современного x86 (за исключением Silvermont / KNL), но inc mem
(без блокировки) стоит дополнительный урок на процессорах Intel: нет микросинтеза нагрузки + добавления мопов, только часть магазина (https://agner.org/optimize/ и https://uops.info/). ИДК, если оно также хуже с lock inc
против lock add
или если вы видите какой-то другой эффект. Инструкция INC против ADD 1: это имеет значение?
Согласно https://uops.info/, lock add m32, imm8
имеет идентичный счетчик мопов (8), задержка и пропускная способность до lock inc m32
на Skylake. В Haswell есть один бэк-энд моп, как разница без префикса блокировки. Но вряд ли это влияет на пропускную способность. Я не проверял другие uarches, и вы не сказали, какой у вас процессор.
Яне очень рекомендую -march=skylake -mtune=generic
: это может решить эту проблему с генерацией кода, но может привести к худшим настройкам в остальной части вашего кода. За исключением того, что он даже не работает, я думаю, что Clang отличается от GCC тем, как он обрабатывает параметры арки и настройки. Я предполагаю, что вы могли бы полностью избежать параметров марша и оставить -mtune
по умолчанию, и просто включить -mavx2 -mfma -mpopcnt -mbmi -mbmi2 -maes -mcx16
и любые другие соответствующие расширения ISA, которые есть у вашего процессора.
и lock xadd
длядругие числа (по крайней мере, для сложения ...
Вы уверены, что все еще включили оптимизацию для нового компилятора?
Когда результат --*p
или atomic_fetch_add(p, -2)
не используетсяClang 9.0 по-прежнему использует lock dec
или lock sub
. Я могу заставить Clang использовать lock xadd
, только если отключу оптимизацию, превращая окружающий код в общий мусор.
Или с включенной оптимизацией, возвращаярезультат. IDK, может быть, в более сложных функциях, clang9.0 изменил что-то, что означает, что он не находит те же оптимизации в вашем коде, и использует lock xadd
, чтобы получить старое значение в регистр. Например, может быть, чтобы вернуть его вызывающемуигнорирует его, если решено не использовать его агрессивно.
lock xadd
определенно медленнее, чем lock add
или lock sub
, но clang не использует его, если не обязан (или если вы отключилиle оптимизация).
вывод asm для clang8.0 -O3 -march=skylake
против clang9.0 -O3 -march=skylake
(не включая ret
) ( Godbolt )
#include <stdatomic.h>
void incmem(int *p) { ++*p; }
clang8: addl $1, (%rdi) clang9: incl (%rdi)
void atomic_inc(_Atomic int *p) { ++*p; }
clang8: lock addl $1, (%rdi) clang9: lock incl (%rdi)
void atomic_dec(_Atomic int *p) { --*p; }
clang8: lock subl $1, (%rdi) clang9: lock decl (%rdi)
void atomic_dec2(_Atomic int *p) {
atomic_fetch_add(p, -2);
}
clang8: lock addl $-2, (%rdi) clang9: lock addl $-2, (%rdi)
// returns the result
int fetch_dec(_Atomic int *p) { return --*p; }
clang8: clang9:
movl $-1, %eax movl $-1, %eax
lock xaddl %eax, (%rdi) lock xaddl %eax, (%rdi)
addl $-1, %eax decl %eax
retq
С отключенной оптимизацией мы получаем, что clang 8 и 9 создают буквально идентичный код с -O0 -march=skylake
:
# both clang8 and 9 with -O0 -march=skylake
atomic_dec2:
pushq %rbp
movq %rsp, %rbp
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
movl $-2, -12(%rbp)
movl -12(%rbp), %ecx
lock xaddl %ecx, (%rax) # even though result is unused
movl %ecx, -16(%rbp)
popq %rbp
retq