利用分块矩阵计算矩阵乘法可以有效利用Cache

以如下矩阵乘法为例解释分块乘法可以有效利用cache。
设:

  • 如下两个 8 ∗ 8 8 *8 88的矩阵 A , B A,B A,B,按 4 ∗ 4 4*4 44进行分块乘法。
  • Cache有12行,每行可以存放4个Int。(目的是使得cache虽然不能装下整个矩阵,但是能装下3个分块矩阵,其中两个是做乘法的矩阵,第三个是结果矩阵)
  • 缓存不命中次数初始值 n = 0 n=0 n=0
    A = [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ] B = [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ] A=\left[ \begin{array}{cccc|cccc} 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ \hline 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ \end{array} \right] \quad B=\left[ \begin{array}{cccc|cccc} 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ \hline 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ \end{array} \right] A=0000000000000000000000000000000000000000000000000000000000000000B=0000000000000000000000000000000000000000000000000000000000000000

A 11 ∗ B 11 A_{11}*B_{11} A11B11为例(分别是 A A A B B B的左上矩阵)

  • 首先是 A 11 A_{11} A11的第一行乘 B 11 B_{11} B11的第一列:先计算 A [ 0 ] [ 0 ] ∗ B [ 0 ] [ 0 ] A[0][0]*B[0][0] A[0][0]B[0][0],这时 A 11 , B 11 A_{11},B_{11} A11B11缓存均不命中,将相应块读入缓存(标红元素表示读入缓存):
    A = [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ] B = [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ] A=\left[ \begin{array}{cccc|cccc} \red{0} & \red{0} & \red{0} & \red{0} & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ \hline 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ \end{array} \right] \quad B=\left[ \begin{array}{cccc|cccc} \red{0} & \red{0} & \red{0} & \red{0} & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ \hline 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ \end{array} \right] A=0000000000000000000000000000000000000000000000000000000000000000B=0000000000000000000000000000000000000000000000000000000000000000
    n = 2 n=2 n=2
  • 接下来 A [ 0 ] [ 1 ] ∗ B [ 1 ] [ 0 ] A[0][1]*B[1][0] A[0][1]B[1][0]直到 A [ 0 ] [ 3 ] ∗ B [ 3 ] [ 0 ] A[0][3]*B[3][0] A[0][3]B[3][0] A 11 A_{11} A11均命中而 B 11 B_{11} B11不命中:
    A = [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ] B = [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ] A=\left[ \begin{array}{cccc|cccc} \red{0} & \red{0} & \red{0} & \red{0} & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ \hline 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ \end{array} \right] \quad B=\left[ \begin{array}{cccc|cccc} \red{0} & \red{0} & \red{0} & \red{0} & 0 & 0 & 0 & 0 \\ \red{0} & \red{0} & \red{0} & \red{0} & 0 & 0 & 0 & 0 \\ \red{0} & \red{0} & \red{0} & \red{0} & 0 & 0 & 0 & 0 \\ \red{0} & \red{0} & \red{0} & \red{0} & 0 & 0 & 0 & 0 \\ \hline 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ \end{array} \right] A=0000000000000000000000000000000000000000000000000000000000000000B=0000000000000000000000000000000000000000000000000000000000000000
    n = 5 n=5 n=5
  • 接下来 A 11 A_{11} A11的第一行依次乘 B 11 B_{11} B11的二、三、四列,由于上一次循环已经把整个 B 11 B_{11} B11缓存进来了,因此全部缓存命中:
    A = [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ] B = [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ] A=\left[ \begin{array}{cccc|cccc} \red{0} & \red{0} & \red{0} & \red{0} & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ \hline 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ \end{array} \right] \quad B=\left[ \begin{array}{cccc|cccc} \red{0} & \red{0} & \red{0} & \red{0} & 0 & 0 & 0 & 0 \\ \red{0} & \red{0} & \red{0} & \red{0} & 0 & 0 & 0 & 0 \\ \red{0} & \red{0} & \red{0} & \red{0} & 0 & 0 & 0 & 0 \\ \red{0} & \red{0} & \red{0} & \red{0} & 0 & 0 & 0 & 0 \\ \hline 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ \end{array} \right] A=0000000000000000000000000000000000000000000000000000000000000000B=0000000000000000000000000000000000000000000000000000000000000000
    n = 5 n=5 n=5
  • 然后 A 11 A_{11} A11的第二行依次乘 B 11 B_{11} B11所有列,在计算 A [ 1 ] [ 0 ] ∗ B [ 0 ] [ 0 ] A[1][0]*B[0][0] A[1][0]B[0][0] A 11 A_{11} A11缓存不命中,需要读入缓存:
    A = [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ] B = [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ] A=\left[ \begin{array}{cccc|cccc} \red{0} & \red{0} & \red{0} & \red{0} & 0 & 0 & 0 & 0 \\ \red{0} & \red{0} & \red{0} & \red{0} & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ \hline 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ \end{array} \right] \quad B=\left[ \begin{array}{cccc|cccc} \red{0} & \red{0} & \red{0} & \red{0} & 0 & 0 & 0 & 0 \\ \red{0} & \red{0} & \red{0} & \red{0} & 0 & 0 & 0 & 0 \\ \red{0} & \red{0} & \red{0} & \red{0} & 0 & 0 & 0 & 0 \\ \red{0} & \red{0} & \red{0} & \red{0} & 0 & 0 & 0 & 0 \\ \hline 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ \end{array} \right] A=0000000000000000000000000000000000000000000000000000000000000000B=0000000000000000000000000000000000000000000000000000000000000000
    n = 6 n=6 n=6
  • A 11 A_{11} A11后两行同理:
    A = [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ] B = [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ] A=\left[ \begin{array}{cccc|cccc} \red{0} & \red{0} & \red{0} & \red{0} & 0 & 0 & 0 & 0 \\ \red{0} & \red{0} & \red{0} & \red{0} & 0 & 0 & 0 & 0 \\ \red{0} & \red{0} & \red{0} & \red{0} & 0 & 0 & 0 & 0 \\ \red{0} & \red{0} & \red{0} & \red{0} & 0 & 0 & 0 & 0 \\ \hline 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ \end{array} \right] \quad B=\left[ \begin{array}{cccc|cccc} \red{0} & \red{0} & \red{0} & \red{0} & 0 & 0 & 0 & 0 \\ \red{0} & \red{0} & \red{0} & \red{0} & 0 & 0 & 0 & 0 \\ \red{0} & \red{0} & \red{0} & \red{0} & 0 & 0 & 0 & 0 \\ \red{0} & \red{0} & \red{0} & \red{0} & 0 & 0 & 0 & 0 \\ \hline 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ \end{array} \right] A=0000000000000000000000000000000000000000000000000000000000000000B=0000000000000000000000000000000000000000000000000000000000000000
    n = 8 n=8 n=8

总结分析

A , B A,B AB n ∗ n n*n nn的矩阵,按 b ∗ b b*b bb进行分块,那么一个矩阵可以划分为 ( n / b ) ∗ ( n / b ) (n/b) * (n/b) (n/b)(n/b) 个分块矩阵,缓存块 = 4 i n t s =4\quad ints =4ints,Cache的容量 C < < n C << n C<<n(一行都放不下), 3 b 2 < C 3b^23b2<C(可以放下三个分块矩阵),那么每一块的不命中次数为 b 2 / 4 b^2/4 b2/4(平均每4个元素发生一次不命中,因为一个缓存块可以放4个元素,一个分块矩阵每行每列为 b b b 个元素),每一次迭代(一次迭代为一整行的分块矩阵乘一整列的分块矩阵)总不命中次数 = 2 n / b ∗ b 2 / 4 = n b / 2 =2n/b*b^2/4=nb/2 =2n/bb2/4=nb/2 n / b n/b n/b表示每行或每列的分块矩阵数,乘2表示两个做运算的矩阵分别不命中),整个矩阵乘法下来总不命中次数 = n b / 2 ∗ ( n / b ) 2 = n 3 / ( 2 b ) =nb/2*(n/b)^2=n^3/(2b) =nb/2(n/b)2=n3/(2b)

结合上面的例子,每一块的不命中次数为 4 2 / 4 = 4 4^2/4=4 42/4=4,每一次迭代不命中数为 8 ∗ 4 / 2 = 16 8*4/2=16 84/2=16次,整个矩阵乘法的不命中次数为 8 3 / ( 2 ∗ 4 ) = 64 8^3/(2*4)=64 83/(24)=64,命中率为 1 − 64 / ( 2 ∗ 8 3 ) = 93.75 % 1-64/(2*8^3)=93.75\% 164/(283)=93.75%

而如果不使用分块乘法,按照最简单的计算方式,总不命中次数为 5 / 4 ∗ 8 3 = 640 5/4*8^3=640 5/483=640,命中率为 1 − 640 / ( 2 ∗ 8 3 ) = 37.5 % 1-640/(2*8^3)=37.5\% 1640/(283)=37.5%

上述例子不太严谨,因为整个Cache是可以放下矩阵的一行的,但是基本原理还是能体现个大概的。

你可能感兴趣的:(计算机系统,考研,矩阵,线性代数,算法)