为什么 A*v+B*v 比 (A+B)*v 快?

计算科学 线性代数 matlab 矩阵
2021-12-11 19:59:58

A矩阵,是具有元素的向量。失败,失败。按照这个逻辑,应该比快。Bn×nvnAv2n2A+Bn2(A+B)vAv+Bv

然而,当我在 matlab 中运行以下代码时

A = rand(2000,2000);
B = rand(2000,2000);
v = rand(2000,1);
tic
D=zeros(size(A));
D = A;
for i =1:100
    D = A + B;
    (D)*v;
end
toc
tic
for i =1:100
    (A*v+B*v);
end
toc

反之亦然。A v+B v 快两倍以上。有什么解释吗?

2个回答

除了对缓存中保存的数据执行大量浮点运算的代码外,大多数浮点密集型代码的性能受限于内存带宽和缓存容量,而不是触发器。

v和乘积都是长度为 2000(双精度为 16K 字节)的向量,很容易放入 1 级缓存。矩阵的大小为 2000 x 2000 或大约 32 兆字节。如果你有一个非常好的处理器,你的 3 级缓存可能足够大来存储这些矩阵之一。AvBvAB

计算需要从内存中读取 32 兆字节(对于),读取 16K 字节(对于AvAv) 将中间结果存储在 L1 缓存中,并最终将 16K 字节写入内存。乘法Bv需要相同的工作量。添加两个中间结果以获得最终结果需要少量的工作。总共大约 64 兆字节的读取和少量的写入。

计算(A+B)需要从内存中读取 32 兆字节(用于 A)加上 32 兆字节(用于 B)并写出 32 兆字节(用于 A+B)。然后你必须像上面那样做一个矩阵向量乘法,它涉及从内存中读取 32 兆字节(如果你有一个大的 L3 缓存,那么这 32 兆字节可能在那个 L3 缓存中。)总共有 96 兆字节读取和 32 兆字节的写入。

因此,计算它所涉及的内存流量是其两倍(A+B)v代替Av+Bv.

请注意,如果您必须使用不同的向量进行许多乘法运算v但同样AB,那么计算效率会更高A+B一次并将该矩阵重用于矩阵向量乘法。

您的代码受内存带宽的限制。对于琐碎的数学,通常最好计算内存访问而不是触发器。你会得到下表:

operation             memory reads/writes

matrix + matrix       3n²
matrix * vector       2n²+n  (if vector is not cached)
matrix * vector       n²+2n  (if vector is only read once)
vector + vector       3n

(A+B)*v (non-cached)  5n²+n       
A*v+B*v (non-cached)  4n²+5n

(A+B)*v (cached)      4n²+2n
A*v+B*v (cached)      2n²+7n

如果向量足够小以适合缓存,Av+Bv 确实快两倍。即使没有缓存,Av+Bv 也更快,尽管不是两倍。

另请注意,您已经需要 2n²+2n 次内存访问,只是为了从内存中读取参数并写回结果,因此 Av+Bv 的缓存变体接近最优。