5

我有时会看到这样的优化代码:

if (matrix[t] != 0) {
    matrix[t] = 0;
}

与此代码相反:

matrix[t] = 0;

我想这段代码是以这种方式编写的,以减少 CPU 中的内存带宽。这是对典型 CPU 的良好优化(当值可能已经为 0 时),为什么?

这对 MESI 状态意味着什么:如果我将相同的值写回高速缓存行(写入但不修改),是否存在从共享到修改的状态转换?或者这对于 CPU 来说是否太复杂而无法检测到?

典型的 CPU(或至少一些)是否针对这种情况进行了优化?

4

1 回答 1

4

AFAIK,没有 x86 微架构尝试通过在仍处于共享MESI状态时读取并检查值是否匹配来将存储从存储缓冲区提交到 L1D 。

这通常很少见,并且只值得为热共享变量增加额外的缓存访问周期,因此默认情况下微架构这样做是没有意义的。大多数存储不是共享变量,并且在存储缓冲区中它不知道哪些存储是共享变量。


在值得这样做的情况下(即有时用于共享变量),您必须自己使用if()问题中的代码来完成。这正是该代码的用途,是的,它可以是一个胜利。

如果其他线程很可能比您上次写入共享变量的时间更久,那么避免写入共享变量是一个好主意,因为它总是使所有其他副本无效以使本地 CPU 的行进入修改状态。

在某些情况下,负载 + 分支错误预测的成本可能高于节省成本,尤其是在预测不佳的情况下。(推测性 RFO 甚至可能在检测到错误预测之前使其他副本无效。当然推测性存储实际上不能提交到 L1D,但所有权读取可能会发生 AFAIK。)

再举一个例子,在自旋锁的重试循环中,您总是希望在纯负载 (+ pause) 上旋转,而不是在xchg. 旋转xchglock cmpxchg将继续敲击该缓存行并延迟实际解锁它的代码。


英特尔的优化手册甚至在 TSX 章节中建议了这种优化,以通过避免不必要的存储来减少访问共享变量的其他线程中的事务中止。

// Example 12-1
state = true; // updates every time
var |= flag;

vs.

if (state != true) state = true;
if (!(var & flag)) var |= flag;

对于 TSX,交易中止的成本甚至比额外等待 MESI 的成本更高,因此值得它的机会可能更高。

于 2017-10-17T14:44:55.377 回答