当前位置: 首页 > 知识库问答 >
问题:

为什么要刷新由其他逻辑处理器导致的内存顺序冲突的管道?

云令
2023-03-14

vTune文档将内存顺序机器清除性能事件描述为:

当来自另一个处理器的嗅探请求与管道中数据操作的源匹配时,会发生内存排序(MO)机器清除。在这种情况下,在取消正在进行的加载和存储之前,将清除管道。

然而,我不明白为什么会这样。不同逻辑处理器上的加载和存储之间没有同步顺序
处理器可以在提交所有当前的飞行中数据操作后假装发生了监听。

此处也描述了该问题

每当CPU核心检测到“内存排序冲突”时,就会触发内存排序机器清除。基本上,这意味着一些当前挂起的指令试图访问我们刚刚发现其他CPU内核同时写入的内存。由于这些指令仍被标记为挂起,而“此内存刚刚写入”事件意味着其他一些内核成功完成了写入,因此挂起的指令以及依赖于其结果的所有内容都是不正确的:当我们开始执行这些指令时,我们使用的内存内容版本现在已经过时。所以我们需要把所有的工作都抛出去,重新来过。这是机器,明白了。

但这对我来说毫无意义,CPU不需要重新执行加载队列中的加载,因为非锁定加载/存储没有总顺序。

我发现一个问题是允许重新排序加载:

;foo is 0
mov eax, [foo]    ;inst 1
mov ebx, [foo]    ;inst 2
mov ecx, [foo]    ;inst 3

如果执行顺序为1 3 2,则在3和2之间出现类似于mov[foo]的存储,1会导致

eax = 0
ebx = 1
ecx = 0

这确实违反了内存排序规则。

但是加载不能与加载一起重新排序,那么,当来自另一个内核的监听请求与任何正在运行的加载源匹配时,为什么Intel的CPU会刷新管道<这种行为会防止哪些错误情况?

共有1个答案

韩麒
2023-03-14

尽管x86内存排序模型不允许对WC以外的任何内存类型的加载在程序顺序之外进行全局观察,但该实现实际上允许加载在顺序之外完成。在所有以前的加载完成之前暂停发出加载请求的代价将非常高昂。考虑以下示例:

load X
load Y
load Z

假设行x不存在于缓存层次结构中,必须从内存中获取。但是,Y和Z都存在于L1缓存中。维护x86负载排序要求的一种方法是在负载X获取数据之前不发出负载Y和X。但是,这会使所有依赖于Y和Z的指令停顿,从而导致潜在的巨大性能损失。

文献中已经提出并广泛研究了多种解决方案。英特尔在其所有处理器中实现的解决方案是允许无序发布负载,然后检查是否发生了内存排序冲突,在这种情况下,违反的负载会被重新发布并重播其所有相关指令。但只有在满足以下条件时才会发生这种违规:

  • 加载已完成,而程序顺序中的前一个加载仍在等待其数据,并且两个加载是需要排序的内存类型。
  • 另一个物理或逻辑内核修改了稍后加载读取的行,并且在早期加载获取其数据之前发出加载的逻辑内核已检测到此更改。

当这两种情况都发生时,逻辑核会检测到内存排序冲突。考虑以下示例:

------           ------
core1            core2
------           ------
load rdx, [X]    store [Y], 1
load rbx, [Y]    store [X], 2
add  rdx, rbx
call printf

假设初始状态为:

  • [十] =[Y]=0
  • 包含Y的缓存线已存在于core1的L1D中。但X不存在于core1的私有缓存中
  • X线以可修改的相干状态出现在core2的L1D中,Y线以可共享状态出现在core2的L1D中

根据x86强排序模型,唯一可能的法律结果是0、1和3。特别是,结果2是不合法的。

可能会发生以下一系列事件:

  • Core2为两行发出RFO。X行的RFO将很快完成,但Y行的RFO必须一直到L3才能使core1的私有缓存中的行无效。请注意,core2只能按顺序提交存储,因此X行的存储等待Y行的存储提交。
  • Core1向L1D发出两个加载。来自Y行的加载很快完成,但来自X的加载需要从core2的私有缓存中获取该行。请注意,此时Y的值为零。
  • 行Y从core1的私有缓存中无效,它在core2中的状态被更改为可修改的一致性状态。
  • Core2现在按顺序提交两个存储。
  • 行X从core2转发到core1。
  • Core1从缓存行X加载core2存储的值,即2。
  • core1打印X和Y的总和,即0 2=2。这是一个非法的结果。本质上,core1加载了一个过时的Y值。

为了保持加载顺序,core1的加载缓冲区必须对驻留在其私有缓存中的行进行所有无效检测。当它检测到行Y已失效,而在程序顺序中失效行的已完成加载之前存在挂起的加载时,会发生内存排序冲突,必须重新发布加载,然后才能获取最新的值。请注意,如果行Y在失效之前和从X加载完成之前已从core1的专用缓存中逐出,则它可能无法首先检测到行Y的失效。因此,也需要有一种机制来处理这种情况。

如果core1从未使用加载的一个或两个值,则可能会发生加载顺序冲突,但无法观察到。类似地,如果core2存储到第X行和第Y行的值相同,可能会发生负载顺序冲突,但无法观察到。然而,即使在这些情况下,core1仍然会不必要地重新发出违反的加载并重播其所有依赖项。

 类似资料:
  • 我过去设计过简单的固定块存储管理器(SM)和通用内存管理器。在这两种情况下,我都会在启动时分配一大块堆内存,并反复使用释放的内存,防止频繁调用昂贵的malloc/new调用。 如果我谈论固定块SM(Github链接),那么我实际上已经看到了它带来的性能优势。在我的例子中,随机大小分配大约提高了40%。 但在通用内存管理器(Github链接)(没有内存池)的情况下,我没有看到任何明显的性能提升。我能

  • 我有 启用会话的服务总线主题 此链接说明了如何在使用逻辑应用程序以确保有序处理消息时实现顺序护航模式。 https://docs.microsoft.com/en-us/azure/logic-apps/send-related-messages-sequential-convoy 问题 这一实施只是一项建议还是强制性的?不管是哪种情况,我都想知道原因

  • 问题内容: 我正在使用带有Java的Google AppEngine。当我使用某些数据存储功能时,出现一条错误消息: 我不知道这意味着什么,如何解决它,或者在哪里可以找到有关此错误的文档。谁能帮我?我使用的代码是: 我相信“ ”是指“拥有的,一对多”的关系。 问题答案: 一个持久对象只能由一个PersistenceManager“管理”。在DataNucleus中,这由“ ObjectManage

  • 我使用的是MGSplitViewClass(链接),我很难正确设置UICollectionView的大小。在上述拆分视图中,我在主视图中有一组四个UIView,其中包含更健壮的视图(表视图、标签组和集合视图)。 首先,我尝试在nib中添加UICollectionView,并通过编程创建了一个集合视图控制器,并将其连接到nib的集合视图。我一试collectionViewController,这就失

  • 我正在玩rxjava,发现如果在活动被销毁之前没有完成订阅,则存在内存泄漏的风险,因为“可观察对象保留对上下文的引用”。如果订阅没有取消订阅,则此类情况的演示之一如下所示。已销毁(来源:https://github.com/dlew/android-subscription-leaks/blob/master/app/src/main/java/net/danlew/rxsubscriptions

  • 问题内容: 众所周知,某些Python的数据结构使用 哈希表 存储诸如或的项目。因此,这些对象没有顺序。但似乎,对于某些数字序列,这是不正确的。 例如,请考虑以下示例: 但是,如果我们进行一些小的更改,则无法排序: 所以问题是:Python的哈希函数如何在整数序列上工作? 问题答案: 尽管SO的问题及其顺序有很多问题,但没有人解释哈希函数的算法。 因此,这里您所需要的就是知道python如何计算哈