原子操作(比如比较并交换或原子加/减)的成本是多少?它需要多少个周期?它会暂停SMP或NUMA上的其他处理器,还是会阻塞内存访问?它会刷新乱序CPU中的重排序缓冲区吗?
对缓存会有什么影响?
我对现代流行的CPU(如x86、x86_64、PowerPC、SPARC、Itanium)很感兴趣。
对缓存会有什么影响?
我对现代流行的CPU(如x86、x86_64、PowerPC、SPARC、Itanium)很感兴趣。
过去几天我一直在寻找实际数据,但是没有找到。不过,我做了一些研究,比较了原子操作的成本和缓存未命中的成本。
x86 LOCK前缀的成本(包括用于原子CAS的lock cmpxchg
),在PentiumPro之前(如文档所述),是一个内存访问(类似于缓存未命中),+停止其他处理器的内存操作,+与其他尝试锁定总线的处理器竞争。然而,自从PentiumPro以来,对于正常的写回可缓存内存(所有应用程序处理的内存,除非您直接与硬件交互),只有相关缓存行被阻塞(基于@osgx's answer中的链接)。
即核心延迟回答MESI共享和RFO请求,直到实际lock
ed操作的存储部分完成。这称为“缓存锁定”,仅影响该缓存行。其他核心可以同时加载/存储或甚至CAS其他行。
实际上,CAS操作可能会更加复杂,如此页面所解释的那样,没有时间限制,但有一位可信赖的工程师提供了深入的描述。(至少对于常规用例,在实际执行CAS之前需要进行纯负载。)
在深入讨论之前,我要说的是,LOCK操作成本为一个缓存未命中+可能与同一缓存行上的其他处理器争用,而CAS操作+前面的负载(几乎总是需要除了在互斥锁上,这里您始终CAS 0和1)可以成本为两个缓存未命中。
他解释说,在单个位置上进行load + CAS实际上可能会成本为两个缓存未命中,就像Load-Linked/Store-Conditional一样(请参阅后者)。他的解释基于MESI缓存一致性协议的知识。它为缓存行使用4个状态:M(修改),E(独占),S(共享),I(无效)(因此称为MESI),必要时下面会进行解释。所述场景如下:
lock
操作只需要一个缓存锁。) - Peter Cordes#define LOOP_COUNT 1000
#define UNROLLED_COUNT 100
PUBLIC ProfileDummy
ProfileDummy:
cli
// Get current TSC value into r8
rdtsc
mov r8, rdx
shl r8, 32
or r8, rax
mov rcx, LOOP_COUNT
jmp looper1
.align 16
looper1:
REPEAT UNROLLED_COUNT
// nothing, or add something to compare against
ENDR
dec rcx
jnz looper1
// Put new TSC minus old TSC into rax
rdtsc
shl rdx, 32
or rax, rdx
sub rax, r8
ret
PUBLIC ProfileFunction
ProfileFunction:
cli
rdtsc
mov r8, rdx
shl r8, 32
or r8, rax
mov rcx, LOOP_COUNT
jmp looper2
.align 16
looper2:
REPEAT UNROLLED_COUNT
// Put here the code you want to profile
// make sure it doesn't mess up non-volatiles or r8
lock bts qword ptr [rsp - 8], 1
ENDR
dec rcx
jnz looper2
rdtsc
shl rdx, 32
or rax, rdx
sub rax, r8
ret
LOCK
确实会打开总线信号LOCK#
。这将禁止总线上的其他CPU/设备使用它。LOCK
指令不会每次都进行总线锁定,除非数据在缓存行上不对齐或存在缓存争用。请访问https://rigtorp.se/split-locks/以获取最新的数据。 - Arnaud Bouchez