如果写操作发生在独占缓存访问期间,为什么会出现数据竞争?

sho*_*dze 3 cpu x86-64 atomic instructions mesi

我正在阅读有关 MESI 协议的内容,并且无法理解如果我们对每个写入操作都具有独占访问权限,为什么会出现数据竞争,从而导致其他核心缓存中的缓存行无效?在这个例子中:

CYCLE # CORE 1                        CORE 2
0   reg = load(&counter);   
1   reg = reg + 1;                reg = load(&counter);
2   store(&counter, reg);         reg = reg + 1;
3                                 store(&counter, reg);
Run Code Online (Sandbox Code Playgroud)

据说总体结果是变量仅增加一次,而两个核心都尝试增加它(结果预计为两次)。所以问题是,如果在写入操作期间,两个内核都请求对缓存行的独占访问(因此其他内核“等待”轮到修改,从而也获得独占访问),为什么该变量上存在数据竞争?

Aco*_*gua 6

如果我没看错的话,MESI 只是一个转移注意力的话题:

0   reg = load(&counter);   
Run Code Online (Sandbox Code Playgroud)

counter现在已经被加载到CPU的寄存器中。

1   reg = reg + 1;                reg = load(&counter);
Run Code Online (Sandbox Code Playgroud)

第一个处理器增加了值,第二个处理器加载了旧值。

2   store(&counter, reg);         reg = reg + 1;
Run Code Online (Sandbox Code Playgroud)

第一个处理器存储值,第二个处理器增加其过时的值。

3                                 store(&counter, reg);
Run Code Online (Sandbox Code Playgroud)

第二处理器存储基于过时值的计算结果。

到目前为止应该很清楚了。现在,如果添加 MESI 状态,情况会如何改变:

0   reg = load(&counter);   
Run Code Online (Sandbox Code Playgroud)

counter位于 CPU 1 缓存中,标记为E

1   reg = reg + 1;                reg = load(&counter);
Run Code Online (Sandbox Code Playgroud)

counter仍然驻留在 CPU 1 缓存中,但也会加载到 CPU 2 缓存中。所以两个缓存行都需要标记为S

2   store(&counter, reg);         reg = reg + 1;
Run Code Online (Sandbox Code Playgroud)

现在counter被存储回缓存中。因此,CPU 1 缓存需要标记为M,而 CPU 2 缓存则失效(标记为I)。

3                                 store(&counter, reg);
Run Code Online (Sandbox Code Playgroud)

由于 CPU 2 缓存失效,因此需要在store操作发生之前对其进行更新,这反过来又要求 CPU 1 缓存之前写回内存(当然)。

但现在所做的一切,reg仍然是根据过时的值计算的,并且仍然覆盖缓存中的(现已更新的)值......

添加最后一个细节:操作完成后,CPU 2 缓存将被标记M为 CPU 1 缓存I

  • @shotasilagadze:一致的缓存不会将单独的读取+添加+写入操作转变为原子RMW增量。但这确实意味着两个不同的线程无法长期读取同一变量的不同值。另外:[其他线程是否总是以相同的顺序看到对不同线程中不同位置的两个原子写入?](//stackoverflow.com/a/50679223) - IRIW 重新排序,其中 2 个读者不同意 2 个读者的存储顺序其他线程通常不能只使用 MESI,除非某些线程可以在存储变得“全局”可见之前看到它们。 (2认同)