同一处理器上两个内核之间的缓存一致性问题
cache coherency issue between two cores on same processor
在不同的内核上分别有两个进程 p1 和 p2 运行 比如说 c1 和 c2(两个内核都在同一个物理处理器上)。这两个核心都具有不同的 L1 和 L2 缓存,同时共享公共的 L3 缓存。 p1 和 p2 都使用指针 ptr(ptr 在共享内存中)。进程 p1 初始化 ptr & p2 应该简单地使用它。面对 p2 的崩溃,因为它最初将 ptr 视为 'NULL'(尽管过了一段时间,可能由于缓存一致性,p2 看到了 ptr 的正确值)。我有以下与此相关的问题:
- 虽然会使用某种形式的缓存一致性协议,但怎么会出现上述情况(p2 看到 ptr 的空值)?
- 在共享 bus/memory 架构的情况下,不同的处理器(在不同的插槽上)通常遵循总线侦听协议以实现缓存一致性。我想知道在两个内核(同一物理处理器上的两个内核)在共享公共 l3 缓存的同时拥有私有 l1/l2 缓存的情况下遵循的缓存一致性协议是什么。
- 有没有办法检查正在使用的缓存一致性协议是什么(这是针对 ubuntu 16.04 系统的)?
x86 即使跨多个套接字也是缓存一致的(就像您可以 运行 std::thread
跨越的所有其他现实世界 ISA)。 x86 的内存排序模型是程序顺序 + 带有存储转发的存储缓冲区。
正式模型:A better x86 memory model: x86-TSO. Informally: http://preshing.com/20120930/weak-vs-strong-memory-models/
缺乏连贯性绝对不是你的问题。一旦存储提交到一个内核中的 L1d 缓存,其他内核就无法加载旧值。 (因为他们的行副本都已经失效所以做修改的核心可以独占:MESI。)
几乎可以肯定,在 p1 写入共享内存之前,p2 正在读取共享内存。 Coherence 不会自行创建同步。如果 p1 和 p2 都异步连接到共享内存,则没有什么可以阻止 p2 在 p1 写入之前进行读取。
您需要某种数据就绪标志,p2 在读取指针之前用 std::memory_order_acquire
检查该标志。或者只是旋转加载指针,直到看到非 NULL 值。
(在指针的原子加载上使用 mo_acquire
以避免编译时重新排序,或在非 x86 上使用 运行 时间重新排序,稍后使用该指针访问内容。或者真的使用指针只需要 mo_consume
,但编译器将其加强为 mo_acquire
。这在 x86 上没问题;acquire 无论如何都是免费的。)
在不同的内核上分别有两个进程 p1 和 p2 运行 比如说 c1 和 c2(两个内核都在同一个物理处理器上)。这两个核心都具有不同的 L1 和 L2 缓存,同时共享公共的 L3 缓存。 p1 和 p2 都使用指针 ptr(ptr 在共享内存中)。进程 p1 初始化 ptr & p2 应该简单地使用它。面对 p2 的崩溃,因为它最初将 ptr 视为 'NULL'(尽管过了一段时间,可能由于缓存一致性,p2 看到了 ptr 的正确值)。我有以下与此相关的问题:
- 虽然会使用某种形式的缓存一致性协议,但怎么会出现上述情况(p2 看到 ptr 的空值)?
- 在共享 bus/memory 架构的情况下,不同的处理器(在不同的插槽上)通常遵循总线侦听协议以实现缓存一致性。我想知道在两个内核(同一物理处理器上的两个内核)在共享公共 l3 缓存的同时拥有私有 l1/l2 缓存的情况下遵循的缓存一致性协议是什么。
- 有没有办法检查正在使用的缓存一致性协议是什么(这是针对 ubuntu 16.04 系统的)?
x86 即使跨多个套接字也是缓存一致的(就像您可以 运行 std::thread
跨越的所有其他现实世界 ISA)。 x86 的内存排序模型是程序顺序 + 带有存储转发的存储缓冲区。
正式模型:A better x86 memory model: x86-TSO. Informally: http://preshing.com/20120930/weak-vs-strong-memory-models/
缺乏连贯性绝对不是你的问题。一旦存储提交到一个内核中的 L1d 缓存,其他内核就无法加载旧值。 (因为他们的行副本都已经失效所以做修改的核心可以独占:MESI。)
几乎可以肯定,在 p1 写入共享内存之前,p2 正在读取共享内存。 Coherence 不会自行创建同步。如果 p1 和 p2 都异步连接到共享内存,则没有什么可以阻止 p2 在 p1 写入之前进行读取。
您需要某种数据就绪标志,p2 在读取指针之前用 std::memory_order_acquire
检查该标志。或者只是旋转加载指针,直到看到非 NULL 值。
(在指针的原子加载上使用 mo_acquire
以避免编译时重新排序,或在非 x86 上使用 运行 时间重新排序,稍后使用该指针访问内容。或者真的使用指针只需要 mo_consume
,但编译器将其加强为 mo_acquire
。这在 x86 上没问题;acquire 无论如何都是免费的。)