小论内存访问方式对性能的影响

        最近两天有幸接受Intel公司的培训。然后培训的时候讲到cache命中的问题。了解到cpu在从memory中load数据的时候其实是将memory中的地址连续的一段数据都load到cache中的,而这时基于这样一个设想:即当要用到的数据附近的数据就是下一次或者下面几次将要进行的操作的数据。。所以将地址连续的一段数据load到cache中,这样将来操作的时候就可以直接从cache而不是内存中取数据,增加cache命中率,减少数据传输的时间。进而提高程序性能。

      当时还讲到一个具体的例子,,最典型的就是矩阵加法。。因为矩阵加法必须有二重循环,形式如下:

 

  for(size i = 0; i < HEIGHT; ++i){
                for(size j = 0; j < WIDTH; ++j){
                        C[i][j] = A[i][j] + B[i][j];
                }
        }

 

 这是一个不好的实现,因为内层循环变量时j递增时,我们可以具体看看循环里面的操作:

      C[j][i]的访问是跳跃式的前进的(stride),比如j = 1时,我们将会访问C[1][i],这样,C[1][i]附近的数据将会被load到cache,但是j递增之后。我们下一个操作访问的数据时C[2][i],它和C[1][i]地址差距为i,这样C[2][i]不一定在缓存中。还得重新到memory中load。减缓了速度。。

      相反,我们再看看另外一种实现:

 

  for(size i = 0; i < HEIGHT; ++i){
                for(size j = 0; j < WIDTH; ++j){
                        C[i][j] = A[i][j] + B[i][j];
                }
        }

    针对此种实现,对于每一次递增的j,内存地址的增加都是挨着的,所以内存访问效率更高,当然程序速度更快。。

    以下是本人基于上述两种方法对矩阵加法的实现,并对比给出执行时间的差距:

    1.跳跃式访问:

 

#include <stdio.h>
#include<time.h>
#include<stdlib.h>

#define WIDTH 20000
#define HEIGHT 20000
#define MAX 100
template<typename size>
size** matAdd(size** A, size** B){

/*
        size height = sizeof(A)/sizeof(A[0]);
        printf("sizeof(A) is :%d\n",sizeof(A));
        printf("sizeof(A[0]) is:%d\n",sizeof(A[0]));
        printf("height is: %d\n", height);
        size width = sizeof(A[0])/sizeof(A[0][0]);
        printf("width is :%d\n", width);
*/

        size** C = new size*[HEIGHT];
        for(size i = 0; i < HEIGHT; ++i){
                C[i] = new size[WIDTH];
        }
        for(size i = 0; i < HEIGHT; ++i){
                for(size j = 0; j < WIDTH; ++j){
                        C[i][j] = A[i][j] + B[i][j];
                }
        }

        return C;
}

int main(){
        typedef int size;
        size** A = new size*[HEIGHT];
        for(size i = 0; i < HEIGHT; ++i){
                A[i] = new size[WIDTH];
        }
        srand((int)time(0));
        size** B = new size*[HEIGHT];
        for(size i = 0; i < HEIGHT; ++i){
                B[i] = new size[WIDTH];
        }

        for(size i = 0; i < HEIGHT; ++i){
                for(size j = 0; j < WIDTH; ++j){
                        A[i][j] = rand() % MAX;
                        B[i][j] = rand() % MAX;
                }
        }
        size** C = matAdd(A, B);
}

 

 执行时间如下所示:

 time ./matAdd

real    0m57.507s
user    0m54.718s
sys     0m2.781s

 

相对比下:连续访问内存实现方式如下:

 

#include <stdio.h>
#include<time.h>
#include<stdlib.h>

#define WIDTH 20000
#define HEIGHT 20000
#define MAX 100
template<typename size>
size** matAdd(size** A, size** B){

/*
        size height = sizeof(A)/sizeof(A[0]);
        printf("sizeof(A) is :%d\n",sizeof(A));
        printf("sizeof(A[0]) is:%d\n",sizeof(A[0]));
        printf("height is: %d\n", height);
        size width = sizeof(A[0])/sizeof(A[0][0]);
        printf("width is :%d\n", width);
*/

        size** C = new size*[HEIGHT];
        for(size i = 0; i < HEIGHT; ++i){
                C[i] = new size[WIDTH];
        }
        for(size i = 0; i < HEIGHT; ++i){
                for(size j = 0; j < WIDTH; ++j){
                        C[i][j] = A[i][j] + B[i][j];
                }
        }

        return C;
}

int main(){
        typedef int size;
        size** A = new size*[HEIGHT];
        for(size i = 0; i < HEIGHT; ++i){
                A[i] = new size[WIDTH];
        }
        srand((int)time(0));
        size** B = new size*[HEIGHT];
        for(size i = 0; i < HEIGHT; ++i){
                B[i] = new size[WIDTH];
        }

        for(size i = 0; i < HEIGHT; ++i){
                for(size j = 0; j < WIDTH; ++j){
                        A[i][j] = rand() % MAX;
                        B[i][j] = rand() % MAX;
                }
        }
        size** C = matAdd(A, B);
}

   执行时间如下所示:

   time ./matAdd

real    0m22.881s
user    0m20.197s
sys     0m2.680s
由此可见,性能相差一倍有余。这也更加体现出连续访问内存的重要性。。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值