liangbch 发表于 2011-11-8 19:47:09

通过一个程序说明cache命中率对内存访问速度的影响

我们知道,现代的CPU包含cache部件,cache亦称高速缓冲,其中存储了CPU最近访问过的或者将要访问的数据。和通常的RAM相比,cache也是一种存储器,其特点存储速度非常快,但同样容量的cache比同样的RAM成本高了很多,因此不能做的太大。

cache亦采用多级存储结构,通常的CPU有8-64K的一级cache,256K-12M的2级cache,有的CPU还有3级cache.许多现代的CPU的2级cache与CPU同频,因此速度非常快。

按照intel的说法,在通常情况下,对RAM的访问实际上近似等同于对cache的访问,因为在90%以上的情况下,RAM中的数据已经被缓冲到cache. 因为程序对RAM的访问具有局部性,也就是说,下次访问的数据的地址和这次访问的数据的地址相邻,所有当访问d[ i ]时,CPU的后台"程序"自动把和d[ i ]相邻的数据载入cache. 下次访问d[ i+1 ]时,可直接从cache中读写数据. 只用当访问的数据不在cache中,CPU才直接从RAM读取数据,由于RAM的很高的延时,故直接从RAM中读写数据比从cache中读写数据要慢得多。


显然,对数据的顺序访问具有极高的cache命中率,因此对数据的顺序访问的性能几乎可达到内存带宽这个上限。内存带宽指内存和cache间批量数据传递的速率,注意内存带宽和内存延时无必然的联系,因为通常情况下,CPU在后台做内存和cache间数据交换,所有内存的高延时被隐藏了。

反之,对数据的随机访问则使得cache命中率大大降低,一旦cache未命中,CPU不得不与内存之间交换数据,此时,内存的高延时就成为瓶颈。

楼下的代码分别测试 对1M*4,4M*4,16M*4,64M*4的内存块进行顺序读取和随机读取所耗费的时间。其速度比如下(测试机的L2 cache is 6M)

块大小速度比
4M      3.0
16M   7.1
64M   11.2
256M    12.5

liangbch 发表于 2011-11-8 19:50:55


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

#include "currTime.h"

typedef unsigned long DWORD;


void initAccessTable(int accessTab[], int len, int isRand )
{
        int i,idx;

        if (isRand==0)//sequential access
        {
                for (i=0;i<len;i++)
                {
                        accessTab=i;
                }
        }
        else
        {
                char *tmpBuff=(char *)malloc(len);
                memset(tmpBuff,0,len);

                for (i=0;i<len;)
                {
                        idx= ((rand() << 15)+ rand()) % len;
                        if ( tmpBuff==0)
                        {
                                accessTab=idx;
                                tmpBuff=1;
                        }
                }
                free(tmpBuff);
        }
}

int sum(DWORD pBuff[], int accessTab[], int len)
{
        DWORD s=0;
        int i,idx;
        for (i=0;i<len;i++)
        {
                idx=accessTab;
                s += pBuff;
        }
        return s;
}

//mode=0, sequential access
//mode=1, random access
void test_access_RAM(int blk_len, int mode)
{
        DWORD *pBuff=malloc(sizeof(DWORD)*blk_len);
        int *accessTab=malloc(sizeof(int)*blk_len);
        DWORD i,s;
        double t;
       
        for (i=0;i<blk_len;i++)
        {
                pBuff=rand() & 15;
        }
       
        initAccessTable(accessTab, blk_len, mode);
       
        t=currTime();
        s=sum(pBuff,accessTab,blk_len);
        t=currTime()-t;

        printf("s=%d\n",s);
        if ( mode ==0)
                printf("sequential access %d number, it take %.6f ms\n",blk_len,t*1000 );
        else
                printf("random access %d number, it take %.6f ms\n",blk_len,t*1000 );

        free(pBuff);
        free(accessTab);

}

void test()
{
        int blk_len;
        int i,loop;
       
        loop=3;
        blk_len=1024*1024;
       
        for (i=0;i<loop;i++)
        {        test_access_RAM(blk_len,0); }
       
        for (i=0;i<loop;i++)
        {        test_access_RAM(blk_len,1); }
       
        //----------------------------
        blk_len=4*1024*1024;
        for (i=0;i<loop;i++)
        {        test_access_RAM(blk_len,0); }
       
        for (i=0;i<loop;i++)
        {        test_access_RAM(blk_len,1); }
       
        //----------------------------
        blk_len=16*1024*1024;
        for (i=0;i<loop;i++)
        {        test_access_RAM(blk_len,0); }
       
        for (i=0;i<loop;i++)
        {        test_access_RAM(blk_len,1); }
       
        //----------------------------
        blk_len=64*1024*1024;
        for (i=0;i<loop;i++)
        {        test_access_RAM(blk_len,0); }
       
        for (i=0;i<loop;i++)
        {        test_access_RAM(blk_len,1); }
}

int main(int argc, char* argv[])
{
        test();
        return 0;
}

kenmark 发表于 2011-11-9 01:53:05

的确,random access 现在已经不能简单认为是O(1)了,但是细化的研究还是比较欠缺,做cache的还是做编译器的比较多,系统里面大家对cache的认识都比较简化。

最近在思索如果提供给programmer在cache-aware 的programming model,但又不至于增加复杂度
页: [1]
查看完整版本: 通过一个程序说明cache命中率对内存访问速度的影响