CPU Relax指令和C++ 11原语

Sil*_*ler 14 c++ multithreading gcc atomic c++11

我注意到许多使用特定于操作系统的原语实现的无锁算法,例如此处描述的自旋锁(使用特定于Linux的原子原语)通常会使用"cpu relax"指令.通过GCC,可以通过以下方式实现:

asm volatile("pause\n": : :"memory");
Run Code Online (Sandbox Code Playgroud)

具体来说,该指令通常用于while循环自旋锁体中,同时等待变量设置为某个值.

C++ 11似乎没有提供任何类型的便携式"cpu_relax"类型指令.这有什么理由吗?"暂停"声明实际上是否实现了有用的功能?

编辑:

另外,我会问:为什么C++ 11标准委员会决定不包括通用std::cpu_relax()或其他什么?保证便携性是否太难了?

小智 24

PAUSE指令是特定于x86的.它的唯一用途是在自旋锁等待循环中,它在:

改善了自旋等待循环的性能.当执行"自旋等待循环"时,处理器在退出循环时将遭受严重的性能损失,因为它检测到可能的内存顺序违规.PAUSE指令向处理器提供代码序列为自旋等待循环的提示.

也:

在spinwait循环中插入暂停指令可以大大降低处理器的功耗.

将此指令放在自旋锁定循环中的位置也是特定于x86_64的.我不能代表C++ 11标准民间,但我认为他们得出结论认为这个魔法的正确位置在相关库中是合理的......以及实现原子,互斥等所需的所有其他魔法.

注:PAUSE不会释放处理器允许另一个线程来运行.它不是 "低级别" pthread_yield().(虽然在Intel超线程内核上,它确实可以阻止自旋锁线程占用内核.)这个基本功能PAUSE似乎是关闭通常的指令执行优化和流水线操作,这会减慢线程(一点点),但是发现锁是忙的,这降低了触摸锁变量的速率,因此当锁的当前所有者试图继续实际工作时,服务员不会对缓存系统进行捣乱.

请注意,用于"手动滚动"自旋锁,互斥锁等的基元不是特定于操作系统的,而是特定于处理器的.

我不确定我会将"手动"旋转锁称为"无锁"!

FWIW,英特尔推荐的自旋锁(" 英特尔®64和IA-32架构优化参考手册 ")是:

  Spin_Lock:
    CMP   lockvar, 0     // Check if lock is free.
    JE    Get_lock
    PAUSE                // Short delay.
    JMP   Spin_Lock
  Get_Lock:
    MOV   EAX, 1
    XCHG  EAX, lockvar  // Try to get lock.
    CMP   EAX, 0        // Test if successful.
    JNE   Spin_Lock
Run Code Online (Sandbox Code Playgroud)

显然,人们可以使用std::atomic_flag...或者使用来编写可以编译的东西pthread_spin_lock(),在我的机器上是:

  pthread_spin_lock:
    lock decl (%rdi)
    jne    wait
    xor    %eax, %eax
    ret
  wait:
    pause
    cmpl   $0, (%rdi)
    jg     pthread_spin_lock
    jmp    wait
Run Code Online (Sandbox Code Playgroud)

这真是难以理解.