Concurrency X86原子RMW指令是否无等待

Concurrency X86原子RMW指令是否无等待,concurrency,x86,atomic,lockless,wait-free,Concurrency,X86,Atomic,Lockless,Wait Free,在x86上,原子RMW指令(如lock add dword[rdi],1)在现代CPU上使用缓存锁定实现。所以缓存线在指令执行期间被锁定。这是通过在读取值时获取行独占/修改状态来完成的,并且在指令完成之前,CPU不会响应来自其他CPU的MESI请求 有两种并发进度条件,阻塞和非阻塞。原子RMW指令是非阻塞的。CPU硬件在保持缓存锁时不会休眠或执行其他操作(中断发生在原子RMW之前或之后,而不是在RMW期间),在释放缓存线之前,步数有一个有限(且很小)的上限 在理论计算机科学中可分为三种类型: 等

在x86上,原子RMW指令(如
lock add dword[rdi],1
)在现代CPU上使用缓存锁定实现。所以缓存线在指令执行期间被锁定。这是通过在读取值时获取行独占/修改状态来完成的,并且在指令完成之前,CPU不会响应来自其他CPU的MESI请求

有两种并发进度条件,阻塞和非阻塞。原子RMW指令是非阻塞的。CPU硬件在保持缓存锁时不会休眠或执行其他操作(中断发生在原子RMW之前或之后,而不是在RMW期间),在释放缓存线之前,步数有一个有限(且很小)的上限

在理论计算机科学中可分为三种类型:

  • 等待自由:所有线程都将在有限的步骤中取得进展

  • 无锁:至少有一个线程将在有限的步骤中取得进展

  • 无障碍:如果没有争用,线程将在有限的步骤中进行

  • x86提供什么样的担保

    我想它至少是无锁的;如果存在争用,则至少有一个CPU将取得进展

    但是x86是否可以免费等待原子指令?是否每个CPU都能保证在有限的步骤内取得进展,或者一个或多个CPU可能会因饥饿而无限期延迟


    那么,当多个内核在同一缓存线上执行原子操作时会发生什么呢?

    当多个线程锁定同一缓存线时,它们的执行会被序列化。这被称为由于

    这就是问题的根源。与读取相反,写入不能同时执行

    原子读-修改-写指令本身的执行时间是固定的,不取决于争用缓存线的线程数。因此,在x86上它们是

    锁定争用缓存线所需时间的上限与缓存线经历的争用程度成正比

    发件人:

    在某些体系结构中,未选择先执行的操作将暂停(然后由硬件重试,直到成功),而在其他体系结构中,它们将“失败”(用于基于软件的重试)。例如,在英特尔处理器中,如果目标内存位置正忙,则硬件将重试锁定的ADD指令,而必须检查锁定的“比较与交换”操作是否成功(因此软件必须注意失败并重试该操作)

    由于缓存线锁定将不断重试,因此最终所有原子读修改写操作都将成功(该操作是指令加上硬件为锁定缓存线而进行的重试)。
    因此,每个CPU都保证在有限的步骤中取得进展,并且x86上的原子读-修改-写操作作为一个整体是非常重要的

    根据相同的逻辑,x86存储操作是无等待有界的,x86存储指令是无等待填充无关的,而x86负载始终是无等待填充无关的

    然而,作为一个人,UCODE错误可能会导致锁永远停留,在描述算法的味道时,我们不考虑外部因素,而只考虑逻辑本身。


    缓存线锁获取不公平

    选择一个线程获取锁的概率与该线程释放锁的方式成正比。因此,同一核心上的线程比共享二级缓存的线程更有可能获得锁,二级缓存的线程比共享三级缓存的线程更有可能获得锁。然后,较短QPI/UPI/NUMA节点路径上的线程比其他节点路径具有优势,依此类推

    软件锁(旋转锁)也是如此,因为发布发布存储时,它以相同的方式传播


    我在Intel Q4'17台式CPU上运行了一个基准测试,证实了上述所有内容。
    当连续
    lock xadd
    ing在同一内存位置上运行时

    • 在10秒钟内,在不同内核上运行的5个线程中,最快的线程
      lock xadd
      ed是最慢的线程的2.5倍,而在不同双向超线程上运行的10个线程中,它的速度是最慢的线程的3倍
    • 3亿次,平均而言,越来越少的
      lock xadd
      s占用的时间越来越长,在不同内核上运行的5个线程高达1.1ms,在不同双向超线程上运行的10个线程高达193ms

    不同进程之间的运行差异很大。

    考虑一个更一般的问题:如果有多个活动硬件线程,那么x86是否保证每个线程向前推进,而不管其他线程做什么?您提出的问题似乎是关于每个线程同时对重叠内存位置执行原子指令的情况。如果答案是肯定的,那么x86可以被描述为“无等待”。(该术语通常仅用于描述线程同步算法,但无论如何。)

    我认为从体系结构或其实现的角度定义“前进”的含义是很重要的。我不喜欢在定义中使用术语“步骤”,因为不清楚什么是步骤,什么不是步骤。相反,我将使用以下定义:活动硬件线程在按程序顺序完成下一条动态指令时,通过使其退役或在出现错误情况时切换到异常处理程序,向前推进。如果每个活动硬件线程都可以在有限的时间内向前推进,而不管其他线程做什么,也不管每个线程执行什么指令,只要它们不会导致线程变为非活动状态,那么x86将被禁用