【问题标题】:Average latency of atomics cmpxchg instructions on Intel Cpus英特尔 Cpus 上原子 cmpxchg 指令的平均延迟
【发布时间】:2011-05-10 10:19:29
【问题描述】:


我正在为各种英特尔处理器的 lock cmpxchg 指令寻找一些平均延迟的参考。我无法找到关于该主题的任何好的参考资料,任何参考资料都会有很大帮助。

谢谢。

【问题讨论】:

  • 将其视为对主内存的读+写。 OTOH,可能是 Ulrich Drepper 的“每个程序员都应该知道的关于内存的知识”有更多的细节。
  • 除了病态的好奇心之外,您在做什么需要知道平均延迟?
  • @MSN:除了病态的好奇心之外没有太多...我在研究 :-) ...基本上弄清楚具有细粒度同步的应用程序是否有机会在多核上运行,如果有,什么时候....

标签: multithreading x86 atomic lock-free


【解决方案1】:

您可以使用 AIDA64 软件检查指令延迟(但您无法检查要检查的指令,它有一个硬编码的指令列表)。人们将结果发布在http://instlatx64.atw.hu/

根据lock 指令,AIDA64 验证lock add 指令和xchg [mem](即使没有显式锁定prefix,它也始终锁定)。

这里有一些信息。为了比较,我还将为您提供以下指令的延迟:

  • xchg reg1, reg2 未锁定;
  • add 到寄存器和内存。

如您所见,锁定指令在 Haswell-DT 上仅慢 5 倍,在 Kaby Lake-S 上仅比非锁定内存存储慢约 2 倍。

英特尔酷睿 i5-4430,3000 MHz (30 x 100) Haswell-DT

LOCK ADD [m8], r8         L: 5.96ns= 17.8c  T: 7.21ns= 21.58c
LOCK ADD [m16], r16       L: 5.96ns= 17.8c  T: 7.21ns= 21.58c
LOCK ADD [m32], r32       L: 5.96ns= 17.8c  T: 7.21ns= 21.58c
LOCK ADD [m32 + 8], r32   L: 5.96ns= 17.8c  T: 7.21ns= 21.58c
LOCK ADD [m64], r64       L: 5.96ns= 17.8c  T: 7.21ns= 21.58c
LOCK ADD [m64 + 16], r64  L: 5.96ns= 17.8c  T: 7.21ns= 21.58c

XCHG r8, [m8]             L: 5.96ns= 17.8c  T: 7.21ns= 21.58c
XCHG r16, [m16]           L: 5.96ns= 17.8c  T: 7.21ns= 21.58c
XCHG r32, [m32]           L: 5.96ns= 17.8c  T: 7.21ns= 21.58c
XCHG r64, [m64]           L: 5.96ns= 17.8c  T: 7.21ns= 21.58c

ADD r32, 0x04000          L: 0.22ns=  0.9c  T: 0.09ns=  0.36c
ADD r32, 0x08000          L: 0.22ns=  0.9c  T: 0.09ns=  0.36c
ADD r32, 0x10000          L: 0.22ns=  0.9c  T: 0.09ns=  0.36c
ADD r32, 0x20000          L: 0.22ns=  0.9c  T: 0.08ns=  0.34c
ADD r8, r8                L: 0.22ns=  0.9c  T: 0.05ns=  0.23c
ADD r16, r16              L: 0.22ns=  0.9c  T: 0.07ns=  0.29c
ADD r32, r32              L: 0.22ns=  0.9c  T: 0.05ns=  0.23c
ADD r64, r64              L: 0.22ns=  0.9c  T: 0.07ns=  0.29c
ADD r8, [m8]              L: 1.33ns=  5.6c  T: 0.11ns=  0.47c
ADD r16, [m16]            L: 1.33ns=  5.6c  T: 0.11ns=  0.47c
ADD r32, [m32]            L: 1.33ns=  5.6c  T: 0.11ns=  0.47c
ADD r64, [m64]            L: 1.33ns=  5.6c  T: 0.11ns=  0.47c
ADD [m8], r8              L: 1.19ns=  5.0c  T: 0.32ns=  1.33c
ADD [m16], r16            L: 1.19ns=  5.0c  T: 0.21ns=  0.88c
ADD [m32], r32            L: 1.19ns=  5.0c  T: 0.22ns=  0.92c
ADD [m32 + 8], r32        L: 1.19ns=  5.0c  T: 0.22ns=  0.92c
ADD [m64], r64            L: 1.19ns=  5.0c  T: 0.20ns=  0.85c
ADD [m64 + 16], r64       L: 1.19ns=  5.0c  T: 0.18ns=  0.73c

英特尔酷睿 i7-7700K,4700 MHz (47 x 100) Kaby Lake-S

LOCK ADD [m8], r8         L: 4.01ns= 16.8c  T: 5.12ns= 21.50c
LOCK ADD [m16], r16       L: 4.01ns= 16.8c  T: 5.12ns= 21.50c
LOCK ADD [m32], r32       L: 4.01ns= 16.8c  T: 5.12ns= 21.50c
LOCK ADD [m32 + 8], r32   L: 4.01ns= 16.8c  T: 5.12ns= 21.50c
LOCK ADD [m64], r64       L: 4.01ns= 16.8c  T: 5.12ns= 21.50c
LOCK ADD [m64 + 16], r64  L: 4.01ns= 16.8c  T: 5.12ns= 21.50c

XCHG r8, [m8]             L: 4.01ns= 16.8c  T: 5.12ns= 21.50c
XCHG r16, [m16]           L: 4.01ns= 16.8c  T: 5.12ns= 21.50c
XCHG r32, [m32]           L: 4.01ns= 16.8c  T: 5.20ns= 21.83c
XCHG r64, [m64]           L: 4.01ns= 16.8c  T: 5.12ns= 21.50c

ADD r32, 0x04000          L: 0.33ns=  1.0c  T: 0.12ns=  0.36c
ADD r32, 0x08000          L: 0.31ns=  0.9c  T: 0.12ns=  0.37c
ADD r32, 0x10000          L: 0.31ns=  0.9c  T: 0.12ns=  0.36c
ADD r32, 0x20000          L: 0.31ns=  0.9c  T: 0.12ns=  0.36c
ADD r8, r8                L: 0.31ns=  0.9c  T: 0.11ns=  0.34c
ADD r16, r16              L: 0.31ns=  0.9c  T: 0.11ns=  0.32c
ADD r32, r32              L: 0.31ns=  0.9c  T: 0.11ns=  0.34c
ADD r64, r64              L: 0.31ns=  0.9c  T: 0.10ns=  0.31c
ADD r8, [m8]              L: 1.87ns=  5.6c  T: 0.16ns=  0.47c
ADD r16, [m16]            L: 1.87ns=  5.6c  T: 0.16ns=  0.47c
ADD r32, [m32]            L: 1.87ns=  5.6c  T: 0.16ns=  0.47c
ADD r64, [m64]            L: 1.87ns=  5.6c  T: 0.16ns=  0.47c
ADD [m8], r8              L: 1.89ns=  5.7c  T: 0.33ns=  1.00c
ADD [m16], r16            L: 1.87ns=  5.6c  T: 0.26ns=  0.78c
ADD [m32], r32            L: 1.87ns=  5.6c  T: 0.28ns=  0.84c
ADD [m32 + 8], r32        L: 1.89ns=  5.7c  T: 0.26ns=  0.78c
ADD [m64], r64            L: 1.89ns=  5.7c  T: 0.33ns=  1.00c
ADD [m64 + 16], r64       L: 1.89ns=  5.7c  T: 0.24ns=  0.73c

【讨论】:

  • 您如何获得 HSW 的 5 倍和 KBL 的 ~2 倍?事情几乎一模一样。您是否比较了 HSW 的吞吐量和 KBL 的延迟?或者您是在与您未列出的纯商店说明进行比较吗? (ADD [m32], r32 不仅仅是一个商店,它还是一个 RMW)。另请注意,locked 指令是一个完整的内存屏障,这意味着如果周围代码中有大量加载+存储,它的成本会更高,因此只需查看locked 指令的无争用吞吐量没有给出真实的图片。
【解决方案2】:

几个月来我一直在研究指数退避。

CAS 的延迟完全取决于指令是可以从缓存中操作还是必须从内存中操作。通常,给定的内存地址由多个线程(例如,指向队列的条目指针)进行 CAS 处理。如果最近成功的 CAS 是由与当前 CAS 执行器共享缓存的逻辑处理器(L1、L2 或 L3,尽管更高级别当然更慢),那么指令将在缓存上运行并且速度会很快 - a几个周期。如果最近成功的 CAS 是由不与当前执行程序共享缓存的逻辑核心执行的,那么最近的 CASer 的写入将使当前执行程序的缓存行无效,并且需要读取内存 - 这将需要数百个周期。

CAS 操作本身非常快 - 几个周期 - 问题是内存。

【讨论】:

  • "and a memory read is required" 不是这样,访问仍然来自缓存,但需要以共享模式获取缓存行(它之前在另一个处理器中处于独占模式) .
  • 自 Nehalem 以来的 Intel CPU 使用大型 inclusive 共享 L3 缓存来支持一致性流量,因此它应该只需要往返 L3。这仍然很慢,尤其是在多插槽系统上。
【解决方案3】:

最佳 x86 指令延迟参考可能包含在 Agner's optimization manuals 中,基于对各种 Intel/AMD/VIA 芯片的实际经验测量,并经常针对市场上最新的 CPU 进行更新。

很遗憾,我没有在指令延迟表中看到 CMPXCHG 指令,但第 4 页确实说明了:

带有 LOCK 前缀的指令有很长的延迟,这取决于缓存组织和可能的 RAM 速度。如果有多个处理器或内核或直接内存访问 (DMA) 设备,则所有锁定的指令都将锁定高速缓存行以进行独占访问,这可能涉及 RAM 访问。一个 LOCK 前缀通常花费超过一百个时钟周期,即使在单处理器系统上也是如此。这也适用于带有内存操作数的 XCHG 指令。

【讨论】:

  • 我认为这已经过时了。我记得有一次我做过这样的测试。我记得从 Intel Core Duo 处理器开始,lock 前缀的 cost 下降到大约 45 个周期。
  • 现在低于 45 个周期。我刚刚对 LOCK INC 进行了基准测试——它不到 35 个周期(当没有竞争时)
  • Agner 列出了 lock cmpxchg、xchg [mem], reg 和其他几个的延迟/吞吐量/uop 计数。不过,这是最好的情况,并且不能反映内存屏障对周围代码中的加载/存储的影响,或者即使存在轻微争用时的 平均 成本(例如,如果当前CPU没有MESIF/MOESI的M状态行,即使cmpxchg第一次尝试成功。)
【解决方案4】:

我一直在尝试在 NOP 方面对 CAS 和 DCAS 进行基准测试。

我有一些结果,但我还不相信它们 - 验证正在进行中。

目前,我在 Core i5 上看到了 CAS/DCAS 3/5 NOP。在 Xeon 上,我看到 20/22。

这些结果可能完全不正确 - 已警告您。

【讨论】:

  • 请注意,这些值是当使用的数据是核心 L1 缓存时。
  • CAS 表示比较和交换? DCAS 表示双重比较和服务,NOP 表示?
  • CAS 是单字 CAS。 DCAS 是连续的双字 CAS。 NOP 是无操作指令。
  • 谢谢,你能否分享一个关于你提到的 CAS 的链接。我发现有些带有比较和交换,有些带有比较和设置...
  • CAS 是通用的 acroynm。例如,英特尔将其称为“比较和交换”-faydoc.tripod.com/cpu/cmpxchg.htm
【解决方案5】:

这方面的好的参考资料很少(如果有的话),因为变化很大。它基本上取决于一切,包括总线速度、内存速度、处理器速度、处理器数量、周围指令、内存栅栏以及很可能月球和珠穆朗玛峰之间的角度......

如果您有一个非常具体的应用程序,例如已知(固定)硬件、操作环境、实时操作系统和独占控制,那么它可能会很重要。在这种情况下,基准。如果您对软件的运行位置没有这种级别的控制,那么任何测量实际上都毫无意义。

正如these answers 中所讨论的,锁是使用 CAS 实现的,因此如果您可以使用 CAS 而不是锁(这将需要至少两个操作),它会更快(很明显?只是可能)。

你会发现最好的参考是Intel Software Developer's Manuals,但由于有太多的变化,它们不会给你一个实际的数字。但是,他们将描述如何获得最佳性能。处理器数据表(例如 i7 Extreme Edition 的 here,在“技术文档”下)可能会为您提供实际数字(或至少是一个范围)。

【讨论】:

  • 感谢 Zooba,这有帮助。我查看了数据表,但可以找到对 cmpxchg 类型指令的引用。我提出问题的原因是因为这个操作在 core-i3 上运行良好,而在 Xeon 上却很糟糕......不知道为什么会这样......因此正在寻找一些参考......
  • 指令“lock cmpxchg”是 x86上的CAS。
  • FWIW,并非所有锁都需要两次 CAS 操作。 x86 上的许多锁在获取路径上使用CAS 或xchg 实现,但在解锁路径上使用简单存储。 x86 内存模型足够强大,可以让它工作。
猜你喜欢
  • 2018-08-24
  • 2019-08-02
  • 2019-01-15
  • 1970-01-01
  • 2018-04-13
  • 2023-03-06
  • 2020-05-12
  • 2012-11-13
  • 1970-01-01
相关资源
最近更新 更多