CPU 中的相关负载重新排序

2023-12-24

我一直在阅读内存屏障:软件黑客的硬件视图 http://www.puppetmastertrading.com/images/hwViewForSwHackers.pdf,保罗·E·麦肯尼 (Paul E. McKenney) 撰写的一篇非常受欢迎的文章。

该论文强调的一件事是,像 Alpha 这样的弱有序处理器可以重新排序相关负载,这似乎是分区缓存的副作用

论文摘录:

1 struct el *insert(long key, long data)
2 {
3     struct el *p;
4     p = kmalloc(sizeof(*p), GPF_ATOMIC);
5     spin_lock(&mutex);
6     p->next = head.next;
7     p->key = key;
8     p->data = data; 
9     smp_wmb();
10    head.next = p;
11    spin_unlock(&mutex);
12 }
13
14 struct el *search(long key)
15 {
16     struct el *p;
17     p = head.next;
18     while (p != &head) {
19         /* BUG ON ALPHA!!! */
20         if (p->key == key) {
21             return (p);
22         }
23         p = p->next;
24     };
25     return (NULL);
26 }
  1. 有 2 个处理器 CPU0 和 CPU1。
  2. 每个CPU有2个缓存体CB0(奇数地址),CB1(偶数地址)。
  3. Head 位于 CB0,P 位于 CB1。
  4. insert() 具有写屏障,可确保第 6-8 行首先在总线中失效,然后在第 10 行失效。
  5. 然而,执行搜索的另一个处理器可能会轻载CB0而重载CB1。
  6. 这意味着处理器领先于 head 的最新值,但 p 的旧值(因为 p 的失效请求尚未被 CB1 处理。)

问题:看起来所有架构都期望 Alpha 荣誉相关负载。 例如:IA64 可以对以下内容进行重新排序,但相关负载重新排序除外。

  1. 加载后重新排序加载
  2. 存储后重新排序加载
  3. 商店重新排序后
  4. 加载后重新排序的商店
  5. 原子指令通过负载重新排序。
  6. 原子指令与商店重新订购。

这让我想知道需要什么硬件支持来防止相关负载重新排序。

一个可能的答案是所有其他体系结构(IA64)没有分区缓存,因此不会遇到此问题,并且不需要显式硬件支持。

有什么见解吗?


简短回答:

在乱序处理器中,加载存储队列用于跟踪和强制执行内存排序约束。 Alpha 21264 等处理器具有必要的硬件来防止相关负载重新排序,但强制执行这种相关性可能会增加处理器间通信的开销。

长答案:

依赖性跟踪的背景

这可能最好用一个例子来解释。假设您有以下指令序列(为了简单起见,使用伪代码指令):

ST R1, A       // store value in register R1 to memory at address A
LD B, R2       // load value from memory at address B to register R2
ADD R2, 1, R2  // add immediate value 1 to R2 and save result in R2

在此示例中,之间存在依赖关系LDADD操作说明。这ADD读取值R2所以它不能执行,直到LD使该值可用。这种依赖性是通过寄存器实现的,并且处理器的发出逻辑可以跟踪它。

然而,两者之间也可能存在依赖关系。STLD,如果地址A and B我们是一样的。但与之间的依赖不同LDADD,之间可能的依赖关系STLD在发出指令(开始执行)时未知。

处理器不会在问题时尝试检测内存依赖性,而是使用称为加载-存储队列的结构来跟踪它们。该结构的作用是跟踪已发出但尚未退役的指令的挂起加载和存储的地址。如果存在内存顺序冲突,则可以检测到这一点,并且可以从发生冲突的点重新开始执行。

因此,回到伪代码示例,您可以想象这样一种情况:LD在之前执行ST(也许 R1 中所需的值由于某种原因尚未准备好)。但当ST执行它会看到该地址A and B是相同的。所以LD确实应该读取所​​产生的值ST,而不是缓存中已经存在的过时值。结果是LD需要重新执行,以及之后出现的任何指令LD。有多种优化方法可以减少部分开销,但基本思想是成立的。

正如我之前提到的,检测这种依赖性的逻辑存在于所有允许推测执行内存指令的乱序处理器(包括 Alpha 处理器)中。

内存排序规则

然而,内存排序规则不仅仅限制处理器从其自己的内存操作中看到结果的顺序。相反,内存排序规则限制了在一个处理器上执行的内存操作对其他处理器可见的操作的相对顺序。

阿尔法示例

在依赖负载重新排序的情况下,处理器必须跟踪此信息以供自己使用,但 Alpha ISA 不要求它确保其他处理器看到此排序。下面是如何发生这种情况的一个示例(我引用了这个链接 http://www.cs.umd.edu/~pugh/java/memoryModel/AlphaReordering.html)

Initially: p = & x, x = 1, y = 0

    Thread 1         Thread 2
--------------------------------
  y = 1         |    
  memoryBarrier |    i = *p
  p = & y       |
--------------------------------
Can result in: i = 0

目前,该异常行为仅可能出现在基于 21264 的计算机上 系统。显然您必须使用我们的多处理器之一 服务器。最后,你真正看到它的机会非常低, 但这是可能的。

以下是要出现此行为所必须发生的情况。假设T1 在 P1 上运行,T2 在 P2 上运行。 P2 必须缓存位置 y,其值为 0。 P1 执行 y=1,这会导致向 P2 发送“无效 y”。这 invalidate进入P2的传入“探测队列”;随你便 看,问题的出现是因为理论上这会无效 坐在探测队列中,而不在 P2 上执行 MB。无效的是 此时立即确认(即,您不必等待它 在发送之前实际上使 P2 缓存中的副本无效 致谢)。因此,P1可以通过它的MB。它继续 写入 p.现在P2继续读取p。阅读 p 的回复 允许在其传入路径上绕过 P2 上的探测队列(这 允许回复/数据快速返回到 21264,无需 等待之前的传入探测得到服务)。现在,P2可以 取消引用 P 以读取位于其缓存中的 y 的旧值 (P2 的探测队列中的无效 y 仍然存在)。

P2 上的 MB 如何解决这个问题? 21264 刷新其输入探头 每个 MB 都有队列(即,为其中的任何待处理消息提供服务)。 因此,在读取 P 之后,您执行一个 MB,将 inval 拉入 y 一定。并且您无法再看到 y 的旧缓存值。

尽管上述情况在理论上是可能的,但可能性 观察到的问题是极其微小的。原因是 即使你正确设置缓存,P2 也可能有足够的 为探测队列中的消息(即无效)提供服务的机会 在收到“read p”的数据回复之前。尽管如此,如果你 陷入这样一种情况:你在 P2 的探针中放置了很多东西 队列在对 y 的无效之前,则有可能对 p 的回复 返回并绕过此 inval。你会很难 设置场景并实际观察异常情况。

上述内容解决了当前 Alpha 可能如何违反您的规定 显示。由于其他优化,未来的 Alpha 可能会违反它。一 有趣的优化是价值预测。

Summary

所有乱序处理器中都已经存在强制执行相关负载排序所需的基本硬件。但确保所有处理器都能看到这种内存排序会给处理缓存行失效增加额外的限制。它也可能在其他场景中增加额外的限制。然而,在实践中,对于硬件设计人员来说,弱 Alpha 内存模型的潜在优势似乎不值得以软件复杂性和需要更多内存屏障的额外开销为代价。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CPU 中的相关负载重新排序 的相关文章

  • 原子发布可以被“覆盖”吗?

    说我有atomic
  • 如何保证加锁顺序以避免死锁?

    假设有以下 Account 类的两个对象 account1 和 account2 并且有两个线程T1和T2 T1 正在将金额 100 从账户 1 转移到账户 2 如下所示 account1 transfer account2 100 同样
  • 如何以编程方式阻止连接到手持设备的 PC 进行文件同步(从设备抓取文件)?

    如前所述here https stackoverflow com questions 28328063 which branches of the registry does a ce device read 我向手持应用程序添加了代码 以
  • 如何在多台机器上同步本地托管的 Greasemonkey 脚本?

    我希望能够在我使用的所有计算机上访问我的 Greasemonkey 脚本 我已经启用了 启用 Firefox 同步用户脚本 在 Greasemonkey 的设置对话框中进行设置 但后来我读到它仅同步外部托管的脚本 然后我尝试使用以下方法设置
  • MySQL select for update 返回空集,即使存在一行

    我发现 MySQL 的 选择更新 有一个奇怪的问题 我使用的是5 1 45版本 我有两张桌子 mysql gt show create table tag Tabl
  • Windows 服务器上的 PTP 同步(与 Linux 相比) - 可以保证什么精度

    我想知道大家是否知道准确度如何PTP http en wikipedia org wiki Precision Time Protocol在 Windows Server 2008 上可以保证同步 我知道这个线程 Windows 中进程的最
  • MySQL 的锁定和并发

    我目前正在将 Mysql 与 InnoDB 存储引擎用于所有表 所以 我想知道这是否是一个真正的问题以及是否有解决方案 例如 我将使用数据库事务向用户收费 1 检查他的余额 2 减去他的余额 3 将此余额记入某处 4 提交 如果更新发生在
  • Linux 中允许的 c/c++ 最大互斥体数量

    我一直在尝试找出 Linux 中 c c 进程的最大互斥体数量是多少 但没有成功 另外 有没有办法修改这个数字 我正在读的书提到了如何找到Linux中允许的最大线程数以及如何修改这个数字 但没有提到互斥体 检查这个pthread mutex
  • x86 汇编中 cmove 指令的用途?

    反汇编可执行文件时我遇到了cmove操作说明 我已经在互联网上搜索过 但我只发现这是一个有条件的移动 如果源和目的地相等mov发生 我还不明白为什么我需要它 因为它不会改变操作数 它的目的是什么 The CMOVcc指令不比较源和目标 它们
  • Erlang 如何并发处理访问邮箱

    关于如何使用erlang邮箱的信息有很多 但很少找到一篇论文或文档描述erlang如何在VM内部同时实际访问邮箱 据我了解 Erlang VM 必须执行锁定或 CAS 操作以确保消息完整性 erlang幕后有没有什么精巧的方法 我假设您所说
  • 现代缓存中的方式预测

    我们知道 就缓存命中时间而言 直接映射缓存优于集合关联缓存 因为不涉及特定标签的搜索 另一方面 组关联缓存通常比直接映射缓存具有更好的命中率 我读到 现代处理器试图通过使用一种称为路径预测的技术来结合两者的优点 他们预测给定集合中最有可能发
  • 如何检查有多少线程正在等待同步方法解锁

    有什么方法可以检查有多少线程正在等待同步方法解锁 我想知道线程何时调用同步方法 1 有多少线程已经在等待调用该方法 2 一旦调用该方法 需要等待该方法解锁多长时间 解决方案 我使用堆垛机答案解决了这个问题 public class Lock
  • 在 Xcode 4 中锁定文件

    我有一个简单的问题 在 Xcode 3 中 我可以通过单击每个文件顶部的小锁图标来锁定文件 我在 Xcode 4 中缺少这个功能 我想我只是盲目的 你能帮助我吗 该功能在 Xcode 4 中被 部分 终止 即使您可以从 文件 菜单解锁锁定的
  • Delphi是否存在无锁队列“多个生产者-单个消费者”?

    我发现了几个针对单个生产者 单个消费者的实现 但没有找到多个生产者 单个消费者的实现 Delphi是否存在 多个生产者 单个消费者 的无锁队列 无锁队列全线程库 http otl 17slon com支持多个生产者 您可以将它与线程库分开使
  • C# 中的监视器与互斥体[重复]

    这个问题在这里已经有答案了 可能的重复 C 中各种线程同步选项之间有什么区别 https stackoverflow com questions 301160 what are the differences between various
  • 为什么在强度降低乘法和循环进位加法之后,这段代码的执行速度会变慢?

    我正在读书阿格纳 雾 https en wikipedia org wiki Agner Fog s 优化手册 https en wikipedia org wiki Agner Fog Optimization 我遇到了这个例子 doub
  • 监控 Java 应用程序上的锁争用

    我正在尝试创建一个小基准 在 Groovy 中 以显示几个同步方法上的高线程争用 当监控自愿上下文切换时 应该会出现高争用 在 Linux 中 这可以通过 pidstat 来实现 程序如下 class Res private int n s
  • 为什么如果内存组织为字,则程序计数器加 1;如果内存组织为字节,则程序计数器加 2?

    如果在计算机中一条指令是 16 位 并且如果存储器被组织为 16 位字 则通过在当前指令的地址中加 1 来计算下一条指令的地址 如果内存是按字节组织的 可以单独寻址 那么我们需要在当前指令地址上加二 得到顺序执行的下一条指令的地址 为什么会
  • 如何确保使用 Microsoft Sync Framework 同步成功?

    我正在使用微软同步框架 https msdn microsoft com en us sync bb736753 aspx同步两个 Microsoft SQL Server 上的表 我创建了一个测试应用程序 它每秒在远程服务器上的表中生成一
  • 在java中以原子方式获取多个锁

    我有以下代码 注意 为了可读性 我尽可能简化了代码 如果我忘记了任何关键部分 请告诉我 public class User private Relations relations public User relations new Rela

随机推荐