问题标签 [mesi]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
c# - 为什么标准 C# 事件调用模式是线程安全的,没有内存屏障或缓存失效?类似的代码呢?
在 C# 中,这是以线程安全方式调用事件的标准代码:
其中,可能在另一个线程上,编译器生成的 add 方法用于Delegate.Combine
创建新的多播委托实例,然后在编译器生成的字段上设置该实例(使用互锁的比较交换)。
(注意:出于这个问题的目的,我们不关心在事件订阅者中运行的代码。假设它是线程安全的并且在删除时是健壮的。)
在我自己的代码中,我想做类似的事情,沿着这些思路:
哪里this.memberFoo
可以由另一个线程设置。(这只是一个线程,所以我认为它不需要互锁 - 但也许这里有副作用?)
(而且,显然,假设它Foo
是“足够不变的”,以至于我们在这个线程上使用它时没有主动修改它。)
现在我明白了这是线程安全的明显原因:从引用字段读取是原子的。复制到本地确保我们不会得到两个不同的值。(显然只能从 .NET 2.0 保证,但我认为它在任何健全的 .NET 实现中都是安全的?)
但是我不明白的是:被引用的对象实例所占用的内存呢?特别是在缓存一致性方面?如果“编写器”线程在一个 CPU 上执行此操作:
什么保证Foo
分配新的内存不会恰好在“读取器”正在运行的 CPU 的缓存中,并且具有未初始化的值?是什么确保localFoo.baz
(上)不读取垃圾?(跨平台的保证程度如何?在 Mono 上?在 ARM 上?)
如果新创建的 foo 恰好来自池怎么办?
从内存的角度来看,这似乎与新的分配没有什么不同——但也许 .NET 分配器可以使第一个案例发挥作用?
在问这个问题时,我的想法是需要一个内存屏障来确保 - 与其说是内存访问不能移动,因为读取是依赖的 - 而是作为向 CPU 发出信号以刷新任何缓存失效。
我的资料来源是Wikipedia,所以你可以随心所欲。
(我可能推测可能是编写器线程上的 interlocked-compare-exchange使读取器上的缓存无效?或者所有读取都会导致无效?或者指针取消引用会导致无效?我特别关心平台特定的这些东西听起来如何。)
更新:只是为了更明确地说明问题是关于 CPU 缓存失效以及 .NET 提供了什么保证(以及这些保证如何取决于 CPU 架构):
- 假设我们有一个存储在字段
Q
(内存位置)中的引用。 - 在 CPU A(写入器)上,我们在内存位置初始化一个对象
R
,并将引用写入R
到Q
- 在 CPU B(阅读器)上,我们取消引用字段
Q
,并取回内存位置R
- 然后,在 CPU B上,我们从
R
假设 GC 在任何时候都没有运行。没有其他有趣的事情发生。
问题:在A在初始化期间对其进行修改之前,是什么阻止R
了B的缓存,这样当B从它读取时,它会得到陈旧的值,尽管它得到了一个新版本的to know where is in place? R
Q
R
(替代措辞:是什么使修改对R
CPU B 在更改对 CPU B可见时或之前对CPU BQ
可见。)
(这是否仅适用于用 分配的内存new
或任何内存?)+
注意:我在这里发布了一个自我回答。
caching - 为什么不能提供从一个处理器到另一个处理器的缓存的直接访问?
在 NUMA 架构(非统一内存访问)中,每个处理器都有自己的一级缓存,因此有一个用于处理器通信的协议 (MESI)。但是为什么每个处理器不能直接连接到其他的缓存呢?我读到“连接根本不够快”,但这并没有解释太多。
谢谢。
c# - 更快的廉价线程安全计数器?
我已阅读此主题:C# Thread safe fast(est) counter并在我的并行代码中实现了此功能。据我所见,一切正常,但是它显着增加了处理时间,大约增加了 10%。
这一直困扰着我,我认为问题在于我正在对小数据片段执行大量相对便宜(<1 个量子)的任务,这些小数据片段被很好地分割并且可能充分利用了缓存局部性,从而以最佳方式运行。根据我对 MESI 的了解,我最好的猜测是 x86LOCK
前缀Interlocked.Increment
将缓存线推入独占模式并强制其他内核上的缓存未命中并强制在每个并行通道上重新加载缓存,只是为了增加这个计数器。由于缓存未命中的 100ns 延迟和我的工作量,它似乎加起来了。(再一次,我可能是错的)
现在,我看不到解决方法,但也许我遗漏了一些明显的东西。我什至在考虑使用 n 个计数器(对应于并行化程度),然后在特定内核上递增每个计数器,但这似乎不可行(检测我在哪个内核上可能会更昂贵,更不用说复杂的 if/then/else结构并弄乱执行管道)。关于如何打破这个野兽的任何想法?:)
caching - 如果使用带有直写策略的缓存,哪些 MESI 协议状态是相关的?
在阅读有关缓存一致性协议的讲座幻灯片时,我遇到了以下问题:如果使用带有直写策略的缓存,哪些 MESI 状态是相关的?
也给出了答案:I (Invalid)和S (Shared Unmodified)。
我知道状态M(Modified Exclusive)是不相关的,因为带有直写策略的缓存无论如何都会将更改传播到主内存。
状态E (Exclusive Unmodified)是不相关的,因为它仅在发生替换的独占读取未命中时发布(并且与进一步的读取命中一起保留)。
有人可以解释给定的答案吗?
caching - Intel 和 AMD 使用哪种缓存一致性协议?
对于我的学士论文,我必须分析虚假共享对多核系统的影响。因此,寻找我在维基百科上遇到的不同缓存一致性协议类型,英特尔开发了 MESIF 缓存一致性协议,但没有信息表明英特尔也使用它。
查看手册英特尔® 64 和 IA-32 架构开发人员手册:卷。3A除了 MESI 协议,我找不到任何关于 MESIF 的信息。所以问题是,英特尔不使用自己的缓存一致性协议。还是我在错误的文档中搜索它。
cpu-architecture - MESI-读取当前正在修改的数据时会发生什么?
如果我有一个数据缓存行并且第一个字节正在被原子修改,我是否仍然可以同时从该缓存行读取不同字节的数据?或者我是否会尝试阅读有关正在发生的原子更新并等待它?
我试图了解上述场景的性能影响。
c++ - 跨多个线程共享数据的 MESI 成本
我想测量读取另一个线程正在写入的变量的缓存一致性(MESI)成本。我提出了以下测试,但它报告读取平均只需要 2 个周期:
我的测试不正确吗?有人可以帮我改进吗?
编辑:我的理解是 MESI 与 C++ 中的原子类型无关。MESI 确保高速缓存行在多个 CPU 内核之间保持一致。因此我在这个测试中没有使用原子。
这与错误共享问题非常相似,我将有第二个全局变量,它们都占用相同的缓存行但不同的线程写入每个。由于每个变量都是由一个线程编写的,我们为什么要使用原子?
caching - 为什么 MESI 协议需要独占状态
我现在正在学习缓存一致性,但我不太明白 MESI 协议中独占状态的功能是什么,因为我认为 MSI 也很好用。
multithreading - 缓存一致性如何在多核和多处理器架构中工作?
让我解释一下我的理解,并请您确认其正确性或纠正我:
- 有一个 MESI 协议允许有效的缓存一致性(https://en.wikipedia.org/wiki/MESI_protocol)。这是最先进的机制。
- 对于单个处理器的多个内核,MESI 通过在处理器内核之间共享的 L3 高速缓存运行。
- 对于多个处理器(没有共享 L3),MESI 通过主存储器运行。
- 当使用由多个线程读写的全局变量时,volatile类型说明符用于防止不需要的优化以及防止缓存在寄存器中(不是在 L1-3 缓存中)。因此,如果值不在寄存器中,而是在高速缓存或主内存中,MESI 将尽其所能让线程看到正确的全局值。
caching - MESI 协议。写入缓存未命中。为什么需要获取主存值?
我想知道使用写入未命中策略分配写入的MESI协议实现。假设我们有写请求并且没有其他缓存行副本的缓存未命中。该图表示下一步是从主内存(或二级缓存)中获取值,将其存储并将缓存行标记为 M(已修改)。我想然后新值存储在缓存块中。问题是:为什么我们需要从主存中获取数据的步骤?为什么我们不能简单地将新值写入 I(无效)状态的第一个找到的缓存行/替换最旧的缓存行并将其标记为 M(修改)?
谢谢您的帮助!