cache block 矩阵矩阵乘

1.   请编写两个1024*1024大小的矩阵(矩阵元素为double类型)相乘的程序。要求采用分块相乘的技术以提高效率,并分析分块方式和Cache容量之间的关系。

解:程序如下: // 由于参数发生了修改,请修正答案。

#include <iostream.h>

#include <time.h>

const int block_size=16; //控制矩阵相乘的块的大小

const int array_size=1024;

const int subArray_size=(int)array_size/block_size;

//求得子数组在每行/列的个数;

int temp_array[block_size][block_size];

int array1[array_size][array_size];

int array2[array_size][array_size];

int result[array_size][array_size];

 

void temp_mul(int x1,int y1,int x2,int y2)//结果放到temp_array[][]中;

{

  for(int i=0;i<block_size;i++)

    for(intj=0;j<block_size;j++)  

     {

        for(intk=0;k<block_size;k++)

        {

          temp_array[i][j] +=array1[x1+i][y1+k] * array2[x2+k][y2+j];       

}    

}

 

void matrix_mul()

{

 /*分块相乘

for(int i=0; i<subArray_size; i++)

    for(int j=0;j<subArray_size; j++)

      {

          for(int k=0;k<subArray_size; k++)

          {

             temp_mul(i*block_size,k*block_size,k*block_size,j*block_size);

             for(intl=0;l<block_size;l++)

               for(intm=0;m<block_size;m++)

               {

                result[i*block_size+l][j*block_size+m] += temp_array[l][m];

                 temp_array[l][m]=0;  } } }*/ /*

int temp;

for(int i0=0; i0<array_size; i0++) //完成array2的转置

   for(int j0=0; j0<i0; j0++)

     {

      temp=array2[i0][j0];

     array2[i0][j0]=array2[j0][i0];

      array2[j0][i0]=temp;           }

   

for(int i1=0; i1<array_size; i1++) //按行相乘

   for(int j1=0; j1<array_size;j1++)

      {

         for(intk1=0;k1<array_size; k1++)

          {

            result[i1][j1]+=array1[i1][k1]*array2[j1][k1]; } } } */

 

int temp1;

for(int i2=0; i2<array_size; i2++) //完成array1的转置

   for(int j2=0; j2<i2; j2++)

     {

      temp1=array1[i2][j2];

     array1[i2][j2]=array1[j2][i2];

      array1[j2][i2]=temp1;  }                                                                                                                                              

                                                                                                                                              

for(int i3=0; i3<array_size; i3++) //按列相乘

   for(int j3=0; j3<array_size;j3++)

      {

         for(int k2=0;k2<array_size;k2++)

          {

            result[i3][j3]+=array1[k2][i3]*array2[k2][j3]; }} }

 

int main(int argc, char* argv[])

{

   for(int x=0;x<array_size;x++)

     for(inty=0;y<array_size;y++)

      {

        array1[x][y]=1;

        array2[x][y]=2;

         result[x][y]=0;   }

    array1[0][0]=2;

    array1[0][1]=4;

    array1[1][0]=6;

    array1[1][1]=8;

                                                                                                                                              

    array2[0][0]=3;

    array2[0][1]=3;

    array2[1][0]=9;

    array2[1][1]=27;

 

   clock_t start, finish;

   double  duration;

   start=clock();

   matrix_mul();

   finish=clock();

  duration=(double)(finish-start)/CLOCKS_PER_SEC;

   cout<<"duration="<<duration<<endl;

 

 cout<<result[4][6]<<endl;//用于验证矩阵计算程序的正确性;

 cout<<result[7][3]<<endl;

 cout<<result[5][1]<<endl;

 cout<<result[2][0]<<endl;  }

 

运行上面的程序,可以通过其中的result矩阵的几个元素result[4][6]=4096;

Result[7][3]=4096; result[5][1]=4122; result[2][0]=4104;从而可以验证了对于分块相乘,按行相乘,按列相乘的程序代码的正确性。

以上代码运行在:Intel (R)Pentium (R)4, 1400MHZ

                L1-D cache 64 KB

                L2-cache  128KB

运行时间是:

按行相乘是:155.77  S

按列相乘是:1183.05  S

 

分块大小

运行时间(S)

2

769.77

4

418.79

8

252.2

16

200.77

32

206.69

64

918.68

可见:按行相乘的性能最好;按列相乘的性能最差;按块相乘,当块大小是16的时候性能最好,而无论块更大还是更小,性能都会差一些。

 

原因分析:

   按行相乘时候,无论对于array1还是array2, miss一次而从内存中取到cache中的数据都会被用到,所以cache的使用率最好。 而对于array2求转置仅仅每个元素使用一次,相对于2048的矩阵相乘带来的好处而言,转置引起的miss非常值得。从而总体的miss 次数最少,从而运行时间最少。

而按列相乘恰好相反,首先对array1求转置就需要一定的miss 开销,同时转置对于矩阵相乘而言并没有带来任何的好处,它造成了array1和array2一样,每次随miss元素而取出的元素都没有被用到,所以造成了cache的使用效率是最低的。从而它总体的miss次数最多,这样运行时间也最长。

对于按块相乘而言,当块大小是16的时候,子矩阵一行的大小是:16*4B=64B恰好是我的机器的L1 data cache 的块大小,因为miss一次所取到cache的数据全部立即被用到,因而这时候矩阵相乘cache的使用效率是最高的,miss的次数也是最少的,所以此时运行时间相对于块更大或更小而言是最少的;但是对于块更小而言,一次取来的数据当时不会立即用到,从而之后再用到的时候可能已经被剔除了cache;而当块更大的时候,一次每计算一行都需要造成多次cache的miss,这样,由于cache总大小有限,就会造成计算结果在cache 和内存间的频频移动,所以miss次数也是很大的,而且随着子矩阵的变大,而miss次数也会变大,从而导致了cache 块更大或更小运行时间都会明显增加。


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值