精华内容
下载资源
问答
  • 一个采用直接映射方式的32KB缓存,假设块长为8个32位的字,且CPU访问缓存命中,则地址为ABCDEF(H)的主存单元在缓存的第几块内? 存储器课后题的一道原题,这道题主要考察主存-缓存 直接映射 的主存和缓存的存储原理...

    一个采用直接映射方式的32KB缓存,假设块长为8个32位的字,且CPU访问缓存命中,则地址为ABCDEF(H)的主存单元在缓存的第几块内?

    存储器课后题的一道原题,这道题主要考察主存-缓存 直接映射 的主存和缓存的存储原理:

    解题如下:
    在这里插入图片描述

    知识原理:

    直接映射原理: 缓存的块内字长等于主存的块内字长,都处于地址的最低位;缓存的子块个数2^c个即C位地址数对应主存C位地址,如图:
    在这里插入图片描述

    全相联映射:
    在这里插入图片描述

    组相联映射原理:缓存的块内字长等于主存的块内字长,都处于地址的最低位;缓存的分组个数R位地址数也对应主存R位地址。如图:
    在这里插入图片描述

    展开全文
  • 1,一个采用直接映射方式的32KB缓存,假设块长为8个32位的字,且CPU访问缓存命中, 则主存地址为ABCDEFH的单元在缓存的第(H)块内。 转换: ABCDEF(16)=1010 1011 1100 1101 1110 1111(2) 求出字块位数: 8...

    1,一个采用直接映射方式的32KB缓存,假设块长为8个32位的字,且CPU访问缓存命中,

    则主存地址为ABCDEFH的单元在缓存的第(H)块内。

    转换:

    ABCDEF(16)=1010 1011 1100 1101 1110 1111(2)

    求出字块位数:

    8个32位的字:(8*32bit)/(8bit/B)=32B;2的5次方等于32B。

    寻址空间为2的5次方。块内地址占主存地址后面5位!

    求出缓存块数:

    32KB/32B=1k=2的10次方。缓存地址占块内地址前面10位!

    开始分割:1010 1011 1100 1101 1110 1111【红色为缓存地址。蓝色为块内地址】

    取出红色的部分:100 1101 111→0010 0110 1111【黑色为补上的0】

    转换为16进制:26FH

     

    2,一个四路组相连的缓存,容量为16KB,假设块长为4个32位的字,

    则地址为FEDCBAH的主存单元映射到缓存的第()【10进制表示】组内。

    与上面同样道理。(所以就不废话啦-)

    FEDCBA(16)=1111 1110 1101 1100 1011 1010(2)

    块内地址位数:(4*32bit)/(8bit/B)=16B;16等于2的4次方,块内地址4位;

    但是,组相连需要提出n位地址放在主存地址里面用来选择组号!

    4=2的2次方;提取出2位;

    缓存块数:16KB/16B=1k;10-2=8;缓存地址占块内地址前面8位!

    (1100 1011)(2)=(128+64+8+2+1=203)(10)

    缓存地址:203;

    OK!

     

     

    展开全文
  • 直接映射高速缓存命中率问题的模拟

            前一节初步介绍了高速缓存的结构和地址划分策略,以及高速缓存“读”处理规则,这一节从讨论“写”开始。在讲解之前,先来复习两个概念:

            时间局部性(Temporal Locality):正在被访问的数据,近期它很可能被再次访问。(比如抖音推送近期喜爱的专题)

            空间局部性(Spatial Locality):正在被访问的数据,其存储空间的相邻空间在短期未来也可能被访问。

            两个概念都针对正在被访问的信息而言。区别是时间局部性强调被访问的数据本身,而空间局部性强调存数据的空间本身。

    一、高速缓存写的处理

            缓存处理读的过程是,根据编号查找相应的值,如果不命中,就从下一集缓存调入新的数据,再根据替换策略(不细数),将新数据替换缓存中的旧数据。而对于缓存处理写的情况,稍复杂些。

            假如CPU要对缓存中某个存在的块进行写操作,什么时候才去更新内存里的与之对应的该字段呢?如果内存和缓存同时更新,这称为直写,即高速缓存只是用来增加“读命中”,而不关心“写命中”。但如果都这样设计,那写操作对于缓存设计又有什么优势呢?所以更常见的方法是,你这个块先委托缓存保管,你写你的,修改你的。什么时候等该块要被驱逐出缓存时,再更新到内存,这称为写回,当然也可能在系统总线空闲时写回,总之写回是有条件的更新。从缓存的思想上看,这是非常美妙的策略,增加了时间局部性优势,减少低层存储设备的读取时间。但是,实现写回意味着更复杂的逻辑,比如每个缓存行都要维护一个修改位用于识别块是否被修改。  

            还有个问题,如果写不命中呢?CPU修改缓存里的某个字段时,发现这个字段不在缓存内,这时到底是从内存把字段读进缓存再修改缓存里的块,还是直接修改内存呢?前者利用空间局部性,但增加步骤,称为写分配,可以理解为分配缓存进行写;同理,后者就成为非写分配,即避开高速缓存直接写到下级存储器。很明显,直写一般就对应非写分配,而写回就对应写分配。有人问直写不是内存缓存一起写么?个人立即是因为直写只是保证内存和缓存不出现矛盾,而不矛盾的情况有两种:要么该数据在缓存和有内存中都有,那必须一致才不矛盾;要么缓存里压根没内存中的数据,这也叫做不矛盾:)

            一般来说,写回+写分配的策略是主流,随着逻辑电路密度的提高,高复杂性的实现越来越容易。虽然采用直写实现起来更简单,但是增加了总线事物,存储层次越往下走,数据传送的耗时越长,因此越可能采用写回而不是直写策略。

    二、高速缓存分配策略

            我们要讨论缓存对程序优化的影响,就要考虑缓存的不命中率(miss rate),还要考虑各级存储的不命中率处罚。一般L1不命中,需要5~10个周期从L2获得数据;L2和L3类似,而缓存不命中从内存获得数据则需要耗费25~100个周期。

            高速缓存的总大小与性能的关系可以做定性讨论。一方面,高速缓存越大,命中率肯定更高;但另一方面,高速缓存更大,命中时间也越长,因此不同的缓存可能采用不同的策略。比如L1,要求不命中处罚时间只能几个周期,那么L1就不能采用大缓存,否则当L1缓存大到一定境界,查找时间都接近不命中处罚时间了。

            高速缓存的块大小和行数量(相联度)与性能的关系也可以做定性讨论。一方面,有较大的块,每行存的数据就更大,而缓存中这些不同的块可能对应完全不相干的低层存储区域,相邻行之间的块数据可能毫不相干,因此本行的数据越大,则代表某个区域的数据集体被缓存的就越多,这是利用了空间局部性(相邻空间大概率被连续访问,其实也可以理解成顺序局部性);但另一方面,高速缓存总大小一旦确定,块越大就意味着行越少,行越少,就意味着缓存可能映射更少计算模块种类的低层存储区域,这就破坏了时间局部性(比如在多进程调用中,你始终只照顾了少数进程模块的计算并用大块给予良好的空间局部性,但大部分时间其他进程模块频繁造成命中,时间局部性就很差)。所谓优化时间局部性即当缓存某块数据后,在短时间内尽可能多重复使用这些数据,而CPU调用的数据可能涉及到存储区很多不同区域,如果你的行太少,覆盖的各存储区域就不够广泛,自然也就很难保证更多区域的数据能够及时被缓存并且被重复使用,即便你利用空间局部性在缓存某几个区域数据时让他们被缓存得更完整。当应对不同数据处理顺序导致某些情况造成不命中的区域增多时,程序实际运行效率参数就可能出现明显的抖动现象。事实上,时间局部性比空间局部性有更好的命中率,如果从命中率来考虑,那么时间局部性的优先级更高。

            然而我们不能无限制的提高行数(提高相联度参数E),如果行数太多,需要更多的标记位,实现起来更昂贵并且访问速度很难提高,实现复杂度的增加会使得其命中时间加大,而且不命中处罚也很大,因为选择牺牲行(缓存慢时需要清除旧行移入不命中新行)的复杂性也增加了。

            以上的分配策略讨论,也是CPU生产商经过权衡依据,他们在设计L1、L2甚至L3时,会反映出他们的折中结果,而且这个结果除了依据理论推导外,还会引入大量的程序运行实践。

     

    三、直接映射高速缓存命中率问题的模拟

            这里详细套用教材上两个很有意思的练习题。

            1、转置矩阵行列互换的实现:

            typedef int array [2][2];

            void transpose(array dst, array src)

            {

                    int  i, j;

                    for(i = 0; i < 2; i++){

                            for(j = 0; j < 2; j++){

                                    dst[j][i] = src[i][j];

                            }

                    }

            }

            假设代码运行环境:

            (1)sizeof(int)= 4。

            (2)src数组从地址0开始,dst数组从地址16开始。

            (3)只有一个L1缓存,是直接映射的、直写、写分配,块大小为8字节。

            (4)高速缓存总大小16字节,一开始空的。

            (5)对src和dst数组的访问分别是读和写不命中的唯一原因。

            

            关于这个例子,很明显能看到,缓存块大小8字节,缓存总大小16字节,说明只有两组,同时也是说两行,而两个数组dst和src在存储上是平权的,这两个二维数组,一共就两行,每行两列,每列2个int型数据,那么每行刚好就是8字节,二维数组总大小16字节。也就是说,整个缓存只能存下一个二维数组,而现在有dst和src两个二维数组,为了实现缓存对两个二维数组的缓存,只能把缓存平均分配给两个二维数组,结果就是缓存可能完整存其中一个数组,也可能两个数组各存一半。

            比如,缓存的第一行8字节用来缓存dst[0][0]、dst[0][1]或者src[0][0]、src[0][1];而缓存的第二行8个字节用来缓存dst[1][0]、dst[1][1]或者src[1][0]、src[1][1]。也就是说,缓存有可能出现以下四种满的情况:

     

    src[0][0]src[0][1]
    src[1][0]src[1][1]

     

    dst[0][0]dst[0][1]
    dst[1][0]dst[1][1]

     

     

    src[0][0]src[0][1]
    dst[1][0]dst[1][1]

     

     

    dst[0][0]dst[0][1]
    src[1][0]src[1][1]


            从上可看出,缓存的行,刚好对应了二维数组的行,至于是哪个二维数组不重要,反正都可以共享缓存的行,有了这样的分配方式,我们就能很容易分析出命中关系了。

     

    由于C语言是从右到左的计算顺序,先读取的是src[0][0],src[0]这一行进缓存:

     

    src[0][0]src[0][1]
      


    接下来是写入dst[0][0],由于都是第一行,因此dst[0]就会占用缓存第一行,而移出src[0]:

     

    dst[0][0]dst[0][1]
      


    然后又会读取src[0][1],我去,刚才src[0][1]还在缓存呆着,不幸被dst[0]取代了,现在又要读取src[0],所以果断移出dst[0]:

     

    src[0][0]src[0][1]
      


    然后又是写入dst[1][0],缓存第二行终于第一次被用上:

     

    src[0][0]src[0][1]
    dst[1][0]dst[1][1]


    接下来是读取src[1][0],哦豁,dst[1]只能滚蛋:

     

    src[0][0]src[0][1]
    src[1][0]src[1][1]


    然后就是写入dst[0][1],以牙还牙,赶出src[0]:

     

    dst[0][0]dst[0][1]
    src[1][0]src[1][1]


    接下来是读取src[1][1],老天有眼,缓存里终于有个现成的了,读取命中!缓存不变:

     

    dst[0][0]dst[0][1]
    src[1][0]src[1][1]

     

    然后就是写入dst[1][1],又把src[1]取代:

     

     

    dst[0][0]dst[0][1]
    dst[1][0]dst[1][1]


            至此循环结束,我们发现,无论读写几乎全部不命中,唯一的例外就是读取src[1][1]时缓存命中。如果我们增加缓存的行,让缓存大小增加到32字节呢?那么dst和src两个二维数组的两个行就可以映射到不同的缓存空间中:

     

    src[0][0]src[0][1]
    src[1][0]src[1][1]
    dst[0][0]dst[0][1]
    dst[1][0]dst[1][1]

            这样一来会不会全部命中呢?No!要知道还有冷不命中(第一次读取铁定不命中好伐?)的概念,缓存为空时,第一次读取肯定是不命中的,有兴趣的读者可以使用四行缓存重复上面的步骤,最后你将会得出,dst[0][0]、src[0][0]、dst[1][0]、src[1][0]四个读或者写时产生冷不命中,而其余四个读写都会是命中!

     

            有了上面的例子做帮助,再来研究教材里的这一课后习题(SimAquarium游戏核心代码)的例子就会容易许多了:

            2、紧密循环实现的命中,假设条件:

            (1)直接映射高速缓存,块大小B=16字节,总大小1024字节的直接映射数据缓存(也就说每组一行,共2^10÷2^4=2^6那么多行);

            (2)sizeof(int)=4,grid从存储器地址0开始

            (3)高速缓存开始时是空的,唯一内存访问是对数组grid的元素访问,其余临时变量都存放在寄存器中。

     

    struct algae_position {

            int x;
            int y;
    };

    struct algae_position grid[16][16];
    int total_x = 0, total_y = 0;
    int i, j;

     for (i = 0; i < 16; i++) {
         for (j = 0; j < 16; j++) {
             total_x += grid[i][j].x;
         }
     }

     for (i = 0; i < 16; i++) {
         for (j = 0; j < 16; j++) {
             total_y += grid[i][j].y;
         }
    }

            总读数是多少?缓存不命中的读总数是多少?不命中率是多少?如果高速缓存的行有两倍大,不命中率会是多少呢?

            我们看看,高速缓存块大小2^4字节,总大小2^10字节,说明有2^6也就是64个缓存行(也是缓存组),缓存结构(S,B,E,m)= (64,16,64,8)。

            grid内每个数组元素是8个字节,那么一个缓存块就能容纳两个数组元素,总共16*16=256个元素就需要128个缓存块,而缓存总大小只有64个块(64个行)。

            从代码的调用能看出是顺序访问,不会重复访问相同的grid行,当读取grid[0][0]时,grid[0][1]也会被读入缓存,当读到grid[7][14]时,grid[7][15]也会被读入缓存,此时缓存刚好满:每个数组行(g[n][16])要占据8个缓存行(1个缓存行存2个数组元素),因此8个数组行(g[0~7][16])就刚好填满64个缓存行),当此时我们继续读取grid[8][0]时,它会取代grid[0][0]、grid[0][1]所占据的第一个缓存块——反正也不需要的,因此,冷不命中和命中将交替出现,x和y的访问都是完全相同的操作:

     

    g[0][0]g[0][1]
    g[0][2]g[0][3]
    …………
    g[7][14]g[7][15]

     

    grid[8][0]进来时,会驱赶grid[0][0]这行:

     

    g[8][0]g[8][1]
    g[0][2]g[0][3]
    …………
    g[7][14]g[7][15]

     

    x全部读取完时,缓存内的情况就如下:

     

    g[8][0]g[8][1]
    g[8][2]g[8][3]
    …………
    g[15][14]g[15][15]

    然鹅悲催的是,当读y时,重复x的步骤,又需要从grid[0][0]读取,于是缓存内的数据一个也用不上!缓存又重复了以上三个步骤。说了半天,所谓命中的部分,只有读g[n][1]、g[n][3]、g[n][5]……g[n][15]时命中,末尾数组标为偶数时,全都是冷不命中。

     

            有了上面的铺垫,就很容易回答问题了。总读数是512(2*16*16),缓存不命中的读总数是256,不命中率是50%,如果高速缓存的行数有两倍多,不命中率会是多少呢?缓存行再大,读x时有区别么?没有,因为你肯定是存在冷不命中的,所以不命中率永远是50%,但读y时,程序可以享受完整的二维数组缓存,因此读y时命中率应该是100%,总命中率就应该是75%。

     

            如果把例题代码改成如下情形,会有怎样的结果呢?

     for (i = 0; i < 16; i++){
         for (j = 0; j < 16; j++) {
             total_x += grid[j][i].x;
             total_y += grid[j][i].y;
         }
     }

            很明显,访问顺序变了!这段代码的外层循环是按列进行遍历,里层循环是遍历行,这样的遍历方式和二维数组的存储顺序相悖,用上面的基础,我们再来详细分析一下:

            如果还按上面循环1的顺序,是类似grid[0][0]~grid[0][15],grid[1][0]~grid[1][15]的遍历方式,就像之前那样,缓存结果会这样:

     

    g[0][0]g[0][1]
    g[0][2]g[0][3]
    …………
    g[0][14]g[0][15]
    g[1][0]g[1][1]
    g[1][2]g[1][3]
    …………
    g[1][14]g[1][15]


            而现在读取方式是grid[0][0]~grid[1][0],什么不会变呢?我想应该是缓存与二维数组的数据空间对应关系不变,当读取grid[0][0],grid[1][0]时,缓存应该是这样:

     

    g[0][0]g[0][1]
    空……空……
    空……空……
    空……
    g[1][0]g[1][1]
    空……空……
    空……空……
    空……空……


            在缓存g[1][0]时,CPU给g[0][3]~g[0][15]预留了对应的缓存空间,因此g[1][0]和g[1][1]刚好缓存在整个g[0]行之后的位置,往下g[2]和g[3]都会给前一行预留足够的空间,即便这时程序并没有读上一行的其他列。
            以此类推,当读到g[8][0]时,缓存虽然还有很多空闲,但却没有对应的位置去存储g[8][0],要存储只能取代g[0][0]:

     

    g[8][0]g[8][1]
    空……空……
    空……空……
    空……空……
    g[1][0]g[1][1]
    空……空……
    空……空……
    空……空……
    g[2][0]g[2][1]
    空……空……
    空……空……
    g[3][0]g[3][1]
    空……空……
    空……空……
    g[7][0]g[7][1]
    空……空……


            我们可以预见到,当程序很久以后要读g[0][1]时,由于不命中又会把g[0][0]、g[0][1]移入缓存,这时候肯定是驱逐g[8][0]、g[8][1],等到遥远的未来程序再读到g[8][1]时,又出现不命中,也就是说,所有g[j][i].x的调用都将是冷不命中或者不命中,只有读取g[j][i].y时,能沾沾光x的光,实现命中,因此,总读数是512,缓存不命中的读总数是256,不命中率是50%。答案不变!

             如果缓存行数扩大到两倍,是什么结果?可以想象一下,那将是128行,每行16字节,就是2^(4+7)=2048 = 256*8,也就是说整个grid二维数组都能装入缓存,再也不会出现什么g[8]驱逐g[0]、g[9]驱逐g[1]的情形。那么此时只可能发生冷不命中,也就是第一次读取某grid行时加载进缓存的动作。举个例子,当读取g[2][0]时,g[2][1]会一起进缓存并且一直保存到程序终结,那么等到程序外层列循环到1时,g[2][1]也会命中,那么除了g[2][0].x引用不命中外,其余的g[2][0].y、g[2][1].x、g[2][1].y引用都会命中,不命中率就会是25%!


            接下来分析下面的循环3代码:
     for (i = 0; i < 16; i++){
         for (j = 0; j < 16; j++) {
             total_x += grid[i][j].x;
             total_y += grid[i][j].y;
         }
     }
     

            很明显,这个是具有良好时间和空间局部性的循环,如果缓存没有扩大,还是1024字节的话,用心算就能得出(j是偶数):g[i][j].x不命中,g[i][j].y、g[i][j+1].x、g[i][j+1].y都会命中,不命中率是25%!

            如果缓存行数又扩大到两倍多呢?随便你缓存有多大都没用,而即便缓存只有一行,也就是说缓存总共只有16字节,不命中率仍然是25%,因为这个循环的访问顺序是按照二维数组的存储顺序走的,g[i][j]、g[i][j+1]进缓存后也只使用一次,所以驱逐不驱逐对他们的结果没有影响。

            我们发现一个现象,无论怎么改进程序和缓存行大小,不命中率始终无法小于25%,其实关于不命中率的计算,存在一个很有意思的公式:如果一个高速缓存块大小为B字节,那么一个步长为k的应用模式(这个k是以字(wordsize)为单位,而1字又等于4字节,你可以把字理解成int),平均每次循环会有min(1,(wordsize*k)/B)次不命中。取最小值,也就说最惨的情况是每次都不命中,最好的情况,是(wordsize*k)/B次不命中。


            先看第一个循环:
     for (i = 0; i < 16; i++) {
         for (j = 0; j < 16; j++) {
             total_x += grid[i][j].x;
         }
     }
            
            很明显,按步长为8字节访问,说明k是2,最小不命中率应该是:(4*2)/16 = 50%。
            再来看第二个循环:

     for (i = 0; i < 16; i++){
         for (j = 0; j < 16; j++) {
             total_x += grid[j][i].x;
             total_y += grid[j][i].y;
         }
     }

            很明显,这个是按步长4字节访问,说明k是1,最小不命中率应该是:(4*1)/16 = 25%。

            再来看第三个循环:

     for (i = 0; i < 16; i++){
         for (j = 0; j < 16; j++) {
             total_x += grid[i][j].x;
             total_y += grid[i][j].y;
         }
     }

           很明显,这个是按步长4字节访问,说明k是1,最小不命中率应该是:(4*1)/16 = 25%。


           你可能会质疑循环2的计算方式,步长怎么是4字节?不是访问完两列就跳行么?呵呵,题目设计最巧的地方也是这里。如果缓存的行够多,能够存下所有的元素,先访问和后访问又有啥区别呢?想想是不是这个道理吧!从这里我们能得出结论,最小不命中数,有可能跟缓存的大小有关,比如循环2;也可能跟缓存大小无关,比如循环3——哪怕你只有一行都没事,只要有一个块就够用,因此该公式只能算出最小不命中率,也就是最优情况,但如何实现最优,条件是什么?那就具体问题具体分析了。

     

            这也许是网上对该习题最详细深入的解读了。实话说,我在开写这一节内容时,越来越发觉自己对例题程序循环2的缓存机制理解有误。之前我一直以为,当访问g[0][0]时,会把g[0]的整行数据:g[0][0]~g[0][15]全部加载进缓存的相应位置,以为这样才能解释为什么习题答案中说g[8][0]没地方存,要驱逐g[0][0]了。但后来发现不对劲!这个假设与循环1和循环3的实现相悖!(转置矩阵例题倒是没看出与之矛盾~囧)凭啥循环1和循环3在读取g[0][0]的时候就没有把g[0][0]~g[0][15]打包加载进缓存呢?!而且也不符合本节第二部分得出的结论:缓存块越大,空间局部性越好,但时间局部性相应就越差;明明你块大小不够了(一次加载只够存g[0][0]、g[0][1]),居然还能用多余的缓存行接着加载g[0][2]、g[0][3]……,这样一来,缓存的块大或者行大又有啥区别呢??思考纠结了好几日,今天终于茅塞顿开!CPU对缓存的利用一定有自己的分配策略,事先将二维数组的各行各列映射到缓存的相应区域,如果映射不完,就让不同的二维数组“元素对”共享同一缓存块(行),这与转置矩阵的分配策略非常类似!这才是对教材前后内容统一的最合理解释。如果你刚好也在这里被迷惑的话,能看到这部分文字是不是感到很幸运?嘿嘿,这道题我也是搜遍网络也找不到其他解答,还好自己悟出来。通过梳理博客到现在,已经有很多地方温故而知新了O(∩_∩)O~

     

     

    展开全文
  • 存储器直接映射,组相联映射习题及解析

    千次阅读 多人点赞 2019-12-08 19:42:25
    **注: ...1、一个采用直接映射方式的16KB缓存,假设块长为8个32位的字,则地址为FDA459H的主存单元映射到缓存的第 290 (十进制表示)块内。 直接映射: 内存字地址等于缓存(cache)块内地...

    注:
    1B = 1 字节
    1b = 1 位
    1B = 8b
    行 = 块
    总块数 = 总字数/一个块中的字数 = 总字节数/一个块中的字节数
    内存地址位数 = 主存地址(二进制)位数
    主存 或 cache 字地址 = 总字数的的二次方的指数
    字块内地址位数:即一个字块的地址位数(字块即块)

    按字寻址与按字节寻址的区别

    当为 按字寻址 时,字块内地址以字为单位计算
    即 块内地址 = 一个块的字数的二次方的指数
    当为 按字节寻址 时,字块内地址以字节为单位计算
    即 块内地址 = 一个块的字节数的二次方的指数
    其余相同

    直接映射:

    该题为按字节寻址

    1、一个采用直接映射方式的16KB缓存,假设块长为8个32位的字,则地址为FDA459H的主存单元映射到缓存的第________(十进制表示)块内。

    直接映射:
    内存地址中的行号代表缓存的块号
    内存地址

    在这里插入图片描述

    缓存块内地址位数:一个字块的总字节的地址位数:此题块中一个字32位,8个字,则一个块中总字节数为8*(32/8)=32,即2^5个字节,则块内地址为5位

    缓存块数:16KB=>16KB/4B(4B:一个字有32位,一个字节有8位,则一个字有4个字节,即4B,1B=1个字节)=4K个字,一个块有8个字,则共有4K/8=2^9个块。则块号占9位

    标记:FDA459H表示总共6位16进制的数(H不算),转为二进制为24位(1位16进制 = 4位二进制),则主存地址总共24位,标记地址=24-5-9=10位

    因内存行号 = 缓存块号

    则该题的内存地址格式为:
    在这里插入图片描述
    则地址为FDA459H的主存单元,其二进制地址为1111 1101 1010 0100 0101 1001,对应缓存第10 0100 010(即十进制290)块中

    该题为按字寻址

    在这里插入图片描述

    直接映射:
    总块数= 总字数/一个块中的字数
    主存区数 = 主存的总字数/cache的总字数(即一个主存可以分成几个cache,一个区代表一个cache)
    则主存块内字数 = cache块内字数
    主存区内块号 = cache的块号
    主存区内块号的块内地址 = cache的块内地址

    在这里插入图片描述

    在这里插入图片描述

    Cache行号 i 对应主存块号j :i = j mod m

    m为cache块数

    访存规则:存在则命中,不存在则调入或替换

    在这里插入图片描述

    组相联映射:

    组地址位数:缓存的块数 / 组数 ,取结果二次方形式的位数

    该题为按字节寻址

    1、设某机主存容量为16MB,缓存的容量为16KB。每字块有8个字,每个字32位。设计一个四路组相连映射(即缓存每组内共有4个字块)的缓存组织,要求:

    (1)画出主存地址字段中各段的位数。

    在这里插入图片描述

    (2)设缓存初态为空,CPU依次从主存第0、1、2、…、99号单元读出100个字(主存一次读出一个字),并重复此次序读8次,问命中率是多少?
    在这里插入图片描述

    (3)若缓存的速度是主存速度的6倍,试问有缓存和无缓存相比,速度提高多少倍?

    在这里插入图片描述

    该题为按字节寻址

    14.某计算机的Cache共有16块,采用2路组相联映射方式(即每组2块)。每个主存块大小为32字 节,按字节编址。主存129号单元所在主存块应装入到的Cache组号是

    A .0
    B. 2
    C. 4
    D. 6

    块内地址32字节,为5位
    每组两个,共8组,组号占了 3位
    129化为2进制 为1 00(组号) 00001(块内)

    所以应该映射到第4组 的第一块

    展开全文
  • 一、mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。实现这样的映射关系后,进程就可以采用指针的方式读写操作这
  • 磁盘IO:缓存IO、直接IO、内存映射

    万次阅读 2013-04-09 18:40:16
    磁盘IO的几种访问方式如下: 缓存IO  缓存I/O又被称作标准I/O,大多数... 读操作:操作系统检查内核的缓冲区有没有需要的数据,如果已经缓存了,那么就直接缓存中返回;否则从磁盘中读取,然后缓存在操作系统
  • 高速缓存与主存的三种映射方式

    千次阅读 2015-12-01 16:01:15
    全相联映射是指主存中任意一个块都可以映射到cache中任意一个块的方式,也就是说,当主存中的某一块需调入cache时,可根据当时cache的块占用或分配情况,选择一个块给主存块存储,所选的cache块可以是cache中的任意...
  • 三种cache映射方式简单讲解

    千次阅读 2020-06-28 18:33:33
    目录前言直接映射全连接映射组映射 前言 关于cache【cache高速...直接映射简单粗暴,每一个内存块都对应到一个cache行,而且映射的方式也很粗暴。下面通过一张图片了解一下 假设有4个cache行,每行16字节,那么主存中
  • 订单商品数据模型 数据模型分析思路 数据模型分析 查询 对多查询 多对多查询 延迟加载 ...缓存 二级缓存 刷新缓存 mybatis整合ehcache 整合ehcache方法(掌握) 二级应用场景 二级缓存局限性
  • Cache与主存之间的全相联映射,直接映射和组相联映射的区别1.高速缓冲存储器的功能、结构与工作原理 高速缓冲存储器是存在于主存与CPU之间的级存储器, 由静态存储芯片(SRAM)组成,容量比较小但速度比主存高得多...
  • Cache与主存之间的全相联映射、直接映射和组相联映射的区别高速缓冲存储器的功能、结构与工作原理高速缓冲存储器是存在于主存与CPU之间的级存储器, 由静态存储芯片(SRAM)组成,容量比较小但速度比主存高得多,...
  • MyBatis缓存和二级缓存

    万次阅读 多人点赞 2018-09-05 22:44:30
    一级缓存的作用域默认是一个SqlSession。Mybatis默认开启一级缓存。 也就是在同一个SqlSession中,执行相同的查询SQL,第一次会去数据库进行查询,并写到缓存中; 第二次以后是直接缓存中取。 当执行SQL查询中间...
  • cache映射方式

    千次阅读 2012-10-22 11:13:45
    Cache与主存之间的全相联映射,直接映射和组相联映射的区别 1.高速缓冲存储器的功能、结构与工作原理  高速缓冲存储器是存在于主存与CPU之间的级存储器,由静态存储芯片(SRAM)组成,容量比较小但速度比主存高得...
  • Cache的容量很小,它保存的内容只是主存内容的一个子集,且Cache与主存的数据交换是以块为单位的。为了把信息放到Cache中,必须...Cache的地址映射方式直接映射、全相联映射和组相联映射。假设某台计算机主存容量...
  • 【MyBatis】MyBatis缓存和二级缓存

    千次阅读 2018-01-27 19:35:53
    一级缓存的作用域默认是一个SqlSession。Mybatis默认开启一级缓存。 也就是在同一个SqlSession中,执行相同的查询SQL,第一次会去数据库进行查询,并写到缓存中; 第二次以后是直接缓存中取。 当执行SQL查询...
  • 一级缓存(及操作) 概念 *在 Session 接口的实现中包含一系列的 Java 集合, 这些 ...*当session的save()方法持久化一个对象时,该对象被载入缓存, 以后即使程序中不再引用该对象,只要缓存不清空,该对象仍然处于生命
  • Cache与主存之间的全相联映射,直接映射和组相联映射的区别 1.高速缓冲存储器的功能、结构与工作原理  高速缓冲存储器是存在于主存与CPU之间的级存储器,由静态存储芯片(SRAM)组成,容量比较小但速度比主存高得...
  • Cache与主存之间的全相联映射,直接映射和组相联映射的区别 1.高速缓冲存储器的功能、结构与工作原理  高速缓冲存储器是存在于主存与CPU之间的级存储器, 由静态存储芯片(SRAM)组成,容量比较小但速度比主存高得...
  • mybatis是一个持久层框架,是Apache下的开源项目,前身是ibatis,是一个不完全的ORM框架,mybatis提供输入和输出的映射,需要程序员自己手动写SQL语句,mybatis重点对SQL语句进行灵活操作。适用场合:需求变化频繁,...
  • cache-主存的三种映射方式

    千次阅读 2021-05-04 10:00:23
    cache-主存的三种映射方式2.1 全相联映射2.2 直接映射方式2.3 组相联映射方式3. 三种映射方式例题 1. 基本概念 1. 存储系统的体系结构 图片摘自这篇博客:图片来源 2. cache在存储系统中的位置 cache(缓存)...
  • ---- Cache的容量很小,它保存的内容只是主存内容的一个子集,且Cache与主存的数据交换是以块(cache line)为单位的。为了把信息放到Cache中,必须应用某种函数把主存地址定位到Cache中,这称为地址映射。---- 在...
  • NIO的直接缓存区与非直接缓存

    千次阅读 2016-08-16 16:48:30
    NIO的直接缓存区与非直接缓存
  • Cache直接映射、组相连映射以及全相连映射

    千次阅读 多人点赞 2018-03-07 11:01:00
    主存和Cache都分组,主存中一个组内的块数与Cache中的分组数相同,组间采用直接映射,组内采用全相联映射。也就是说,将Cache分成u组,每组v块,主存块存放到哪个组是固定的,至于存到该组哪一块则是灵活的。例如,...
  •  Cache的容量很小,它保存的内容只是主存内容的一个子集,且Cache与主存的数据交换是以块为单位的。为了把信息放到Cache中,必须应用某种函数把主存地址定位到Cache中,这称为地址映射。在信息按这种映射关系装入...
  • mybatis_SQL映射(5)_缓存

    千次阅读 2013-12-23 17:24:10
    找了篇文章,写得比较全面,转了 http://blog.csdn.net/lee4037/article/details/16891327   ...然后是SQL映射文件的配置 eviction="FIFO" flushInterva
  • Caffeine 缓存

    万次阅读 2017-12-13 17:07:05
    简介 在本文中,我们来看看Caffeine— 一个...此策略直接影响缓存的命中率 — 缓存库的一个重要特征。 Caffeine 因使用 Window TinyLfu 回收策略,提供了一个近乎最佳的命中率。 填充策略(Population) Caffein...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 139,669
精华内容 55,867
关键字:

一个采用直接映射方式的缓存