您可以在 clang 的错误跟踪器上报告性能回归,特别是如果您可以使用 GCC9 生成一个较慢的 MCVE / [mcve],原因与您的主代码相同。
如果不使用内联 asm 完全自己完成,通常无法强制编译器的指令选择选择。
但调整选项可能是相关的,例如-march=native 调整您的硬件。 (-march=haswell 或 -march=native on 一个 haswell,例如设置 -mtune=haswell 以及启用您拥有的所有 ISA 扩展。)
例如gcc 或 clang 通常有时会避免使用inc,但在为没有问题的特定 CPU 进行编译时使用它。 (不谈lock 版本,只谈inc reg)。
这里的情况似乎是这样:使用-march=skylake 让clang9.0 使用lock inc 而不是lock add [mem], 1,但在Godbolt 上却没有使用clang8.0。(只有-O3,clang9.0仍然使用lock add/sub)
inc reg 在现代 x86 上很好(Silvermont / KNL 除外),但inc mem(无锁)在 Intel CPU 上需要额外的微指令:没有加载+添加微指令的微融合,只有存储部分(https://agner.org/optimize/ 和 https://uops.info/)。如果lock inc 与lock add 相比更糟,或者您看到其他效果,IDK。 INC instruction vs ADD 1: Does it matter?
根据https://uops.info/,lock add m32, imm8 与 Skylake 上的lock inc m32 有 相同 uop 计数 (8)、延迟和吞吐量。 在 Haswell 上少了一个-end uop,就像没有锁前缀的区别。但这不太可能影响吞吐量。我没有检查其他的uarches,你也没有说你有什么CPU。
我不会真正推荐-march=skylake -mtune=generic:它可能会解决这个代码生成问题,但可能会导致您的其余代码做出更糟糕的调整决策。除了它甚至不起作用之外,我想 clang 在处理拱形和调整选项的方式上与 GCC 不同。我想您可以完全避免使用 March 选项并将 -mtune 保留为默认值,而只需启用 -mavx2 -mfma -mpopcnt -mbmi -mbmi2 -maes -mcx16 和您的 CPU 具有的任何其他相关 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 不会使用它,除非它必须(或者如果你禁用优化)。
clang8.0 -O3 -march=skylake 与 clang9.0 -O3 -march=skylake 的 asm 输出(不包括 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