当一个核心写入其 L1 缓存而另一个核心也在其 L1 缓存中写入相同的行时会发生什么?
What happens when a core write in its L1 cache while another core is having the same line in its L1 too?
当一个内核在其 L1 缓存中写入而另一个内核也在其 L1 中具有相同的行时会发生什么?
假设是英特尔 Skylake CPU。
缓存系统如何保持一致性?它是否实时更新,是否停止其中一个核心?
使用两个内核在同一缓存行中连续写入的性能成本是多少?
一般来说,现代 CPU 使用 MESI 协议的一些变体 1 来保持缓存一致性。
在你的L1写场景中,细节取决于缓存行的现有状态:缓存行是否已经在写入核心的缓存中?在另一个核心中,缓存行处于什么状态,例如是否被修改?
让我们以一个简单的例子为例,其中该行还没有在写入核心(C1)中,并且它在另一个核心(C2)中处于 "exclusive" 状态。在写入地址已知时,C1 将使用线路地址向 "bus" 发出 RFO(所有权请求)事务,其他内核将 snoop 总线并通知交易。具有该行的另一个核心将其行从 exclusive 状态转换为 invalid 状态,并且该行的值将为提供给请求核心,它将使其处于 已修改 状态,此时可以继续写入。
请注意,此时,写入核心对该行的进一步写入会快速进行,因为它处于 M 状态,这意味着不需要发生总线事务。在线路被逐出或其他一些核心请求访问之前,情况都是如此。
现在,在实际实现中有很多额外的细节,上面甚至在协议的维基百科描述中都没有涵盖。
例如,基本模型涉及每个 CPU 的单个私有缓存和共享主内存。在这个模型中,核心 C2 通常会将共享线路的值提供到总线上,即使它没有修改它,因为这比等待从主内存读取值要快得多。然而,在所有最近的 x86 实现中,有一个 shared 最后一级 L3 缓存,它位于所有私有 L2 和 L1 缓存与主内存之间。此缓存通常为 inclusive,因此它可以直接向 C1 提供值,而无需从 C2 进行缓存到缓存的传输。此外,拥有此共享缓存意味着每个 CPU 实际上可能不需要窥探 "bus",因为可以首先查询 L3 缓存以确定哪些内核(如果有的话)实际拥有该行。只有拥有这条线的核心才会被要求进行状态转换。一种推模型而不是拉模型。
尽管有所有这些实现细节,但基本原理是相同的:每个缓存行都有一些 "per core" 状态(即使此状态可能存储或复制在某些中心位置,如 LLC),并且此状态以原子方式进行逻辑转换,以确保缓存行始终保持一致。
鉴于上述背景,这里有一些针对您最后两个子问题的具体答案:
Does it update in real time, does it stop one of the cores?
任何现代内核都会实时执行此操作,并且还会针对不同的缓存行并行执行此操作。这并不意味着它是免费的!例如,在上面的描述中,C1 的写入被暂停,直到缓存一致性协议完成,这可能是几十个周期。将其与只需要几个周期的正常写入进行对比。还可能存在带宽问题:用于实现协议的请求和响应使用可能具有最大吞吐量的共享资源;如果一致性事务的速率超过某个限制,所有请求都可能变慢,即使它们是独立的。
过去,当真正有共享总线时,在某些情况下可能会出现部分 "stop the world" 行为。例如,x86 原子指令的 lock
前缀显然是根据 lock
信号命名的,即 CPU 在执行原子事务时会在总线上断言。在整个期间,其他 CPU 无法完全使用总线(但大概他们仍然可以继续使用 CPU-local 指令)。
What's the performance cost of continuously writing in same cache line with two cores?
成本非常高,因为线路会在两个核心之间不停地乒乓球,如上所述(在描述的过程结束时,只需将C1和C2的角色互换并重新启动)。确切的细节因 CPU 甚至平台而异(例如,2 插槽配置会大大改变此行为),但基本上可能会考虑每次写入 10 秒周期的惩罚,而不是 -每个周期 1 次写入的共享输出。
您可以在 的答案中找到一些具体数字,其中涵盖了 "two threads on the same physical core" 案例和 "separate cores" 案例。
如果您想了解有关特定性能场景的更多详细信息,您可能应该提出一个单独的特定问题,列出您感兴趣的特定行为。
1 MESI 的变体经常会引入新的状态,例如 MOESI or the "Forwarded" state in MESIF 中的 "owned" 状态。这个想法通常是使某些转换或使用模式比普通的 MESI 协议更有效,但基本思想大致相同。
当一个内核在其 L1 缓存中写入而另一个内核也在其 L1 中具有相同的行时会发生什么?
假设是英特尔 Skylake CPU。
缓存系统如何保持一致性?它是否实时更新,是否停止其中一个核心? 使用两个内核在同一缓存行中连续写入的性能成本是多少?
一般来说,现代 CPU 使用 MESI 协议的一些变体 1 来保持缓存一致性。
在你的L1写场景中,细节取决于缓存行的现有状态:缓存行是否已经在写入核心的缓存中?在另一个核心中,缓存行处于什么状态,例如是否被修改?
让我们以一个简单的例子为例,其中该行还没有在写入核心(C1)中,并且它在另一个核心(C2)中处于 "exclusive" 状态。在写入地址已知时,C1 将使用线路地址向 "bus" 发出 RFO(所有权请求)事务,其他内核将 snoop 总线并通知交易。具有该行的另一个核心将其行从 exclusive 状态转换为 invalid 状态,并且该行的值将为提供给请求核心,它将使其处于 已修改 状态,此时可以继续写入。
请注意,此时,写入核心对该行的进一步写入会快速进行,因为它处于 M 状态,这意味着不需要发生总线事务。在线路被逐出或其他一些核心请求访问之前,情况都是如此。
现在,在实际实现中有很多额外的细节,上面甚至在协议的维基百科描述中都没有涵盖。
例如,基本模型涉及每个 CPU 的单个私有缓存和共享主内存。在这个模型中,核心 C2 通常会将共享线路的值提供到总线上,即使它没有修改它,因为这比等待从主内存读取值要快得多。然而,在所有最近的 x86 实现中,有一个 shared 最后一级 L3 缓存,它位于所有私有 L2 和 L1 缓存与主内存之间。此缓存通常为 inclusive,因此它可以直接向 C1 提供值,而无需从 C2 进行缓存到缓存的传输。此外,拥有此共享缓存意味着每个 CPU 实际上可能不需要窥探 "bus",因为可以首先查询 L3 缓存以确定哪些内核(如果有的话)实际拥有该行。只有拥有这条线的核心才会被要求进行状态转换。一种推模型而不是拉模型。
尽管有所有这些实现细节,但基本原理是相同的:每个缓存行都有一些 "per core" 状态(即使此状态可能存储或复制在某些中心位置,如 LLC),并且此状态以原子方式进行逻辑转换,以确保缓存行始终保持一致。
鉴于上述背景,这里有一些针对您最后两个子问题的具体答案:
Does it update in real time, does it stop one of the cores?
任何现代内核都会实时执行此操作,并且还会针对不同的缓存行并行执行此操作。这并不意味着它是免费的!例如,在上面的描述中,C1 的写入被暂停,直到缓存一致性协议完成,这可能是几十个周期。将其与只需要几个周期的正常写入进行对比。还可能存在带宽问题:用于实现协议的请求和响应使用可能具有最大吞吐量的共享资源;如果一致性事务的速率超过某个限制,所有请求都可能变慢,即使它们是独立的。
过去,当真正有共享总线时,在某些情况下可能会出现部分 "stop the world" 行为。例如,x86 原子指令的 lock
前缀显然是根据 lock
信号命名的,即 CPU 在执行原子事务时会在总线上断言。在整个期间,其他 CPU 无法完全使用总线(但大概他们仍然可以继续使用 CPU-local 指令)。
What's the performance cost of continuously writing in same cache line with two cores?
成本非常高,因为线路会在两个核心之间不停地乒乓球,如上所述(在描述的过程结束时,只需将C1和C2的角色互换并重新启动)。确切的细节因 CPU 甚至平台而异(例如,2 插槽配置会大大改变此行为),但基本上可能会考虑每次写入 10 秒周期的惩罚,而不是 -每个周期 1 次写入的共享输出。
您可以在
如果您想了解有关特定性能场景的更多详细信息,您可能应该提出一个单独的特定问题,列出您感兴趣的特定行为。
1 MESI 的变体经常会引入新的状态,例如 MOESI or the "Forwarded" state in MESIF 中的 "owned" 状态。这个想法通常是使某些转换或使用模式比普通的 MESI 协议更有效,但基本思想大致相同。