根据局部性得出最优矩阵乘法写法

读csapp的第6章, 感觉cache hit rate怎么都算得和答案不一样, 越看越看不下去. 不过终于看到一点有意思的内容. 那就是根据局部性来预测不同的矩阵乘法方式的性能.

矩阵乘法, 自然不用说, 3个循环, i,j,k. 其中i表示行, j表示列, k表示中间.

任务就是比较这6个方式的性能.

首先我们先看看写最内层循环的思路. 显然是对i,j,k的全排列.
(a)(b)自然不用说, 是最经典写法, 外层循环换了顺序.
(c)(d)相似, (e)(f)相似, 而(c)与(e)思路相同, 因此仅仅需要讨论(c).
对(c), 固定了jk, 那么bjk就固定了, 变量是i, 因此可以写的是aik, 加在C[i][j]上.

显然也不存在依赖的关系,所有的读取数据都可以并发执行,写指令也一样. 因此唯一影响性能的, 就只有cache了.

然后来看每一个方法的缺失率. 假设n很大(这样的话, 不会发生读到后面, 发现很久之前的块还在的情况), block的大小是32 byte, 元素类型是double. 意味着一次可以取出4个元素. 意味着如果stride=1,也就是取完这个立即取紧挨着的, 缺失率会是0.25

(a) C不会反复取(最内层循环中), 缺失率是0, A是顺序读取, 是0.25. B不是顺序读取, 是1.
(b) 与(a)完全一样, 分析一样.
(c) B不会反复取, 缺失率是0, A不是顺序, 是1, C不是顺序, 是1.
(d) 同(c).
(e) C顺序取, 是0.25, A不会缺失, 是0. B也是顺序取, 0.25.
(f) 同(e)

从这段分析可以看出, 每一行性能相似, 而(c)是最坏的, (e)是最好的. 因此矩阵乘法最优写法是kij.

遗漏了一点, 如果只看操作次数, (a)应优于(e), 因为e最内循环读了2次内存(C,B),写了1次内存(C). 而(a)读了2次内存(A,B), 没有写内存.
但为什么(a)还是不如(e)? 其实应该是有影响的, 虽然是并发写, 但是读和写用的是同一个功能单元. 应该是被cache的差距盖过操作系数的不同.

posted @ 2020-11-09 16:02  Tokubara  阅读(489)  评论(0编辑  收藏  举报