计算机体系结构:编译器预取例题(优化前后失效次数计算)

题目内容(前半部分)

对于下面的程序

for(int i=0;i<3;++i)
{
   for(int j=0;j<100;++j)
   {
      a[i][j]=b[j][0]+b[j+1][0];
   }
}   

假定:
(1)使用一个容量为8KB、块大小为16字节的直接映像数据Cache,采用写回法并且按写分配。忽略指令Cache失效,并假设数据Cache无冲突失效和容量失效。
(2)a、b分别为3×100(3行100列)和101×3的双精度浮点数组,每个元素都是8个字节。当程序开始执行时,这些数据都不在Cache内。在主存中,数组元素按行依次存放。
判断上述程序段中哪些访问可能会导致数据Cache失效。然后,加入预取指令以价绍失效,计算所执行的预取指令的条数以及通过预取避免的失效次数。

题目分析

(1)不进行预取时Cache总失效次数的计算

本题中需要从内存中分别取出数组a和数组b中的数据,因此发生失效的次数就等于访问数组a时Cache的失效次数和访问数组b时Cache的失效次数之和。
对于整个Cache,其中的块数=8KB/16B=500(块)

①访问数组a时Cache的失效次数:
由于块的大小为8个字节,而a和b都是双精度浮点数数组,因此每个元素占8个字节,所以一个块中可以存放两个a或b数组中的元素。
通过分析循环可知,数组a采用的是逐行访问的方式,也就是先从a[0][0]、a[0][1]访问到a[0][99],然后访问a[1][0]、a[1][1]…。通过题目可知数组元素按行进行存放,因此每一次放入Cache中的是一行中相邻的两个元素(如a[0][0]和a[0][1])。
进一步分析,第一次对a访问所需要访问的元素是a[0][0],此时一定发生失效(强制性失效),并把a[0][0]和a[0][1]调入到同一个Cache块中;接下来访问a[0][1],发现可以直接从Cache中命中;再接着访问a[0][2],Cache发生失效,将a[0][2]和a[0][3]调入Cache…
重复上述过程可以看出,当列下标j为偶数时,Cache访问一定失效;当列下标为奇数时(如a[0][1]),都会发生Cache命中。
由于a中一共有300个元素,因此访问a所导致的Cache失效次数可以计算如下:

失效次数(a)=300/2=150(次)

②访问数组b时Cache的失效次数:
分析代码可知,每一次访问数组b,都要取某一行和其下一行的首元素。
由于数组b是101行3列的数组,因此每次访问的两个元素之间还间隔两个元素,所以无法调入同一个Cache块中。
从开始处尝试执行代码:首先调入b[0][0]和b[0][1],两者都不在Cache中,发生两次Cache失效;接着调入b[0][1]和b[0][2],由于b[0][1]已经在上一次循环中未命中后被调入Cache,因此只有b[0][2]发生一次Cache失效;接下来一轮循环中b[0][3]发生一次Cache失效…
以此类推,则可以判断出:由于一次内循环共需要执行100轮,因此执行完一次内循环共发生101次失效。
在下一次进行内循环时,由于此时Cache中块还没有满,所以b[0][0]以及后面的其他b元素仍然在Cache中,因此都不发生失效。

综上所述,访问数组b时Cache共失效101次。

总失效次数=150次+101次=251次

(2)进行预取时Cache总失效次数的计算

本题采用如下假设:假设失效开销较大,使得每一次进行预取都必须至少提前7次循环进行。
为什么失效开销较大就需要提前久一些进行预取呢?这里可以这么理解:如果不提前进行预取,那么等到几乎快要用到这个数据时进行预取已经来不及了。比如预取一个数据需要5个周期,如果提前10个周期进行预取,那自然来得及;但是假如提前3个周期才进行预取,而失效开销又非常大(极端一点,假设失效开销为1000个周期),那么还没取出来就已经发生了失效,带来了严重的失效开销。因此需要未雨绸缪,尽可能早地预取出数据,尽可能避免严重失效开销对于性能的影响
下面分析预取情况:
对于a数组,第一次循环从a[0][0]已知持续到a[0][99],按照提前7个周期进行预取的原则,第一个被预取的元素是a[0][7],第二个被预取的元素是a[0][8]…以此类推即可。按照逻辑共需要取99-7+1=93次,因此发生失效的次数是(100-93)//2+1=4次(分别是a[0][0] a[0][2] a[0][4]和a[0][6]发生失效)。后两次循环的过程类似,仍然是对应行的0号、2号、4号和6号数据发生失效。因此a数组的失效总次数为12次
对于b数组,从b[0][0]和b[1][0]一直取到b[98][0]和b[99][0]。由于第一次循环取出b数组元素后b数组一直放在Cache中,以后各次循环都不失效,因此b数组只在第一次循环失效。被预取出的b数组元素从b[7][0]开始,因此发生失效的元素为b[0][0]-b[6][0]。所以b数组的失效总次数是7次
因此,经过预取处理后Cache的总失效次数为

12+7=19次

可以看出,经过预取处理后Cache失效次数明显降低,但是代价是需要执行400条预取指令(a数组三轮循环共执行300条预取指令,b数组在第一轮循环执行100次预取指令)。

题目内容(后半部分)

在以下条件下,计算上例中所节约的时间。
(1)不考虑Cache失效时,修改前的循环每7个时钟周期循环一次。修改后的程序中,第一个预取循环每9个时钟周期循环一次,而第二个预取循环每8个时钟周期循环一次(包括外层for循环的开销)。
(2)一次失效需100个时钟周期。
(3)假设预取可以被重叠或与Cache失效重叠执行,从而能以最大的存储带宽传送数据。

题目分析

可能你现在还不太理解第一个和第二个预取循环分别表示什么,下面首先分析这一点。
通过上面的分析可以得知:在第一轮循环中,需要进行预取的元素包括a数组中的元素和b数组中的元素;而之后的两轮循环中,都不需要重新预取b数组中的元素而只需要重新取a数组中的元素,所以可以把第一轮循环称为第一个预取循环,之后的循环称为第二个预取循环。
通过时间周期数也可以看出:由于第一个预取循环需要多访问一个数组,因此时钟周期也比第二个预取循环长一些

下面计算优化前后的执行总时钟周期数。
对于优化前不需要进行预取,而循环次数为300次,所以用于循环执行部分的时钟周期数满足

7×300=2100

又通过上例计算出Cache的失效次数为251次,因此总失效开销如下

251×100=25100

因此优化前的总时钟周期数计算如下

25100+2100=27200

接下来计算优化后的时钟周期数。
第一个预取循环执行了100次,第二个预取循环执行了200次,因此用于预取循环的执行周期总数计算如下:
100×9+200×8=2500
由于失效开销产生的时钟周期数计算如下:

19×100=1900

因此,经过预取优化后程序的总时钟周期数如下:

2500+1900=4400

因此可以得出结论,预取后程序性能提高的倍数如下:

27200/4400=6.18(倍)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值