CPU cache学习

来源:互联网 发布:mes软件 编辑:程序博客网 时间:2024/06/05 17:40
计算机中cache的概念无处不在,buffer大多是指软件缓存,而cache多指硬件缓存,我把buffer和cache理解为同一个概念。
经过几天的学习,对cpu cache有点了解,把学习过程记录下来。

cache位于cpu和存储器之间
CPU -fast-> CACHE -slow-> MEMORY
每当cpu执行的指令需要访问存储器时,给出物理地址在地址总线上,cache控制器会根据这个地址判断是否访问的地址中的内容已经在cache中(方式如下),如果存在那么直接把内容传递给cpu,否则从memory中读取CACHE_LINE_SIZE字节大小的数据,这个读取比较慢,具体读写可以优化,选择一个cache中的line放置读取的内容,具体替换的算法不说了。写存储器原理是一样的。

那么怎么根据地址判断是否在cache中呢?以我的机器为例子,奔腾3.0G,memory为512M,也就是0x20000000(2^29),cache的大小为1M(/proc/cpuinfo),CACHE_LINE_SIZE为128字节(1
所以cache line的个数为2^20/2^7 == 2^13 == 8096。
而memory又有多少个cacheline大小的块呢?2^29/2^7 == 2^22

很显然,并不是所有的内容都能放到cache中,1/512 的内容在cache中。 好了,可以说说为怎么判断是否在cache中了(cache命中)举例说明:
当访问256M+1K处的地址时,物理地址值为0x100,00400,看看低20位(由1Mcache计算出来的,所以是20不是21或18)为0000,0000,0100,0000,0000,这下好了,cache控制器知道,如果它在,那么只能在cache中的哪一块呢,01000(低7位不影响,只是偏移),所以硬件电路直接访问那一块,那么如果这个cache块有效,它是对应,0x100,00400(256M+1K),还是0x10,00400(16M+1K),还是其它的呢?对了,这下缓冲管理有个目录,里面,记录了一个称为tag的12bit的值,对于上述例子,如果这个值为0x100那么就表明这个块缓冲着256M+1K的内容了。否则(比如0x010)不是(cache缺失)。
显然,缺失了就要把这个块用真正的内容替换掉,如果必要把内容写回到memory 16M+1K,再读进256M+1K的 128字节到这个块中。接下来的传输到cpu和命中是一样的。这样如果接下来又要访问16M+1K的内容呢,再替换呗,再256M+1K呢,再换,再16M+1K,再换,这就是颠簸啦,可是谁会访问的地址高位(20以上)会变化如此的频繁呢,是的不会,这就是局部性原理,和颠簸是相反的概念。只要前面12位不变(在同一个M内),那么就不会颠簸了。
但是也有可能会有地址变动很大的可能,比如把一块数据从16M copy 到256M,偶尔还是很有可能的把,所以就有了N-WAY,就是说中间13为相同并不一定只是映射到同一个cache line中,可以映射到N个块中,比如N == 2,那么当映射完16M+1K时要映射256M+1K,那么会发现,还有个槽可以映射,那么就不会颠簸了,同理,增大N可以减小更多的颠簸,比如需要检查16M+1k和15M+1K(同样的中间13位),是否相等,再决定是否要copy到256M+1K中。 这下N == 2,还是会颠簸。但是N=4一般就足够了,偶尔不够的可能性就非常小了,不是吗,局部性原理。当然对于1M的cache,N>1之后,2*13次方个函数值就会减少到1/2^(N-1)了。不过,还是因为局部性原理,有什么关系呢。
上述并不一定正确,而且很多细节没有说到,但是网上很多说cache的文章(中文的不多),找个再看看,这里主要描述的主要是我对cache的地址怎么映射的理解,也没有说多级映射,我没太了解。看看下面的例子吧,跑一跑,我自己想出来的,^^,可以根据自己的处理器快慢设置times,时间差大概为50-100倍,因为fine==0时一直在颠簸。通过下面的例子,我相信应该能写出来一个程序,在处理器主频差不多但是cache不一样的机器上跑快慢相差50倍,为什么呢,让程序在小cache的机器上颠簸而大的不颠簸。比如操作的数据总和介于大小cache之间


#include stdlib.h>
#define TIMES 50
char buf1[128][40960];
char buf2[128][40960];
int main(int argc,char *argv[])
{
    int i,j;
    int times = 0;
    int fine = 1;
    if (argc == 2)
        fine = atoi(argv[1]);
    if(fine) {
        while(times++  TIMES)
            for(i=0;isizeof(buf1)/sizeof(buf1[0]);++i)
                for(j=0;jsizeof(buf1[0]);++j) {
                    buf1[j] = buf2[j];
                }
    } else {
        while(times++  TIMES)
            for(j=0;jsizeof(buf1[0]);++j)
                for(i=0;isizeof(buf1)/sizeof(buf1[0]);++i) {
                    buf1[j] = buf2[j];
                }
    }
    return 0;
}