我有时会看到这样的优化代码:
if (matrix[t] != 0) {
matrix[t] = 0;
}
与此代码相反:
matrix[t] = 0;
我认为这段代码是以这种方式编写的,以减少CPU中的内存带宽。这是典型CPU上的一个很好的优化(当值可能已经为0时)以及为什么?
这对于MESI状态意味着什么:是否存在状态转换,例如:如果我将相同的值写回缓存行(写但没有修改),则共享为已修改?或者这对于CPU来说太复杂了?
典型的CPU(或至少是一些)是否优化了这种情况?
答案 0 :(得分:2)
AFAIK,没有x86微体系结构尝试通过读取将商店从商店缓冲区提交到L1D,同时它仍然处于共享MESI状态并检查值是否匹配。
它通常很少见,只值得为热共享变量提供额外的缓存访问周期,因此微架构默认情况下这样做是没有意义的。大多数商店不到共享变量,并且在商店缓冲区内,它不知道哪些商店是共享变量。
如果值得做(即有时候是共享变量),你必须自己使用问题中的if()
之类的代码。这正是该代码的用途,是的,它可以是一个胜利。
最好避免编写共享变量,如果其他线程很可能比你上次编写它时更早读取它,因为它总是使所有其他副本无效以使本地CPU的行进入修改状态。
在某些情况下,负载+分支误预测的成本可能高于保存成本,特别是如果预测不好的话。 (在检测到错误预测之前,推测性RFO甚至可能使其他副本无效。 当然,投机商店实际上不能承诺L1D,但所有权的读取可能发生在AFAIK。)
另一个例子,在自旋锁的重试循环中,你总是希望旋转一个纯负载(+ pause
),而不是xchg
。在xchg
或lock cmpxchg
上旋转将继续锤击该缓存行,并延迟实际解锁它的代码。
Intel's optimization manual甚至建议在TSX章节中进行这种优化,以减少通过避免不必要的存储而进入共享变量的其他线程中的事务中止。
// Example 12-1
state = true; // updates every time
var |= flag;
vs.
if (state != true) state = true;
if (!(var & flag)) var |= flag;
使用多伦多证券交易所,交易中止的成本甚至高于额外等待MESI的成本,因此它的价值可能更高。