何优化使用C6000系列C64x的Cache--原理,Cache种类和优化策略

本主题的第一部分主要以TI C64x DSP为例介绍cache缓存的基本概念, 解释了为什么需要cache,cache如何和主内存进行通信以及如何优化cache的性能。第二部分主要介绍了怎么配置cache以及怎样正确的使用cache,即如何保证cache的一致性。其中有关于DMA的传输怎么影响cache以及怎么管理DMA传输的双缓存。关键字:C64x DSP Cache DMA L1P L1D 直接映射 set-associative;

处理器的cache是一块存储靠近处理器数据的高速存储区。这帮助常用的指令和数据的快速访问从而提高计算性能。Cache可以视为平坦式记忆体,即认为cache是CPU靠近的可以很快访问的存储器,本篇主要是TI的C64x的处理器为例介绍cache的基本概念和cache的基本术语,接下来就是利用cache的特性来进行优化存储提高程序性能和数据吞吐率。

存储组织结构
图1的左边的模型是一个平坦式记忆体系统架构,假设CPU和片内存储空间都运行在300 MHz,存储访问的延时只有在CPU访问外存的时候才存在,而memory stall不会在访问片内存储区时发生。如果CPU的频率是600 MHz,那么在访问这部分片内存储区的时候还是存在等待周期的。不幸的是,想在片内实现足够大的存储区能运行在600 MHz会非常昂贵的,如果仍然让片内的存储区运行在300 MHz,那么访问这些存储区的适合会有一个周期的延时。


图 1. 平坦和分层的存储架构

一个解决方法是使用分层的存储架构,有一个快速的靠近CPU的存储区,访问没有stall但是size很小,往外的内存空间很大,但是离CPU较远,访问需要比较大的stall,靠近CPU的存储区可以视为cache。

访问定位的规律
当然,这种解决方案只有在CPU在大部分的访问都是只针对最靠近它的存储区时才是有效的,幸运的是,根据访问定位的规律,这一条可以保证。访问的定位规律表明程序在一个相对小的时间窗口对仅需要一个相对较小size的数据和代码。数据定位的两条规律:

  • 空间关联性:当一个数据被访问时,它临近的数据又很大可能会被后续的存储访问;
  • 时间关联性:一个存储区被访问时,在下一个临近的时间点还会被访问。

空间关联性揭示了计算机程序的创建规律:通常情况下相关的数据被编译链接到临近的连续区域。例如首先处理一个数组的第一个元素,然后处理第二个,这就是空间关联性。类似的,时间关联性主要源于程序中存在占用时间非常多的循环,通常循环的代码被连续执行非常多次,一般循环内访问的数据也相当。


图 2. 存储访问定位的规律

图2是空间关联性的说明,一个6-tap的FIR滤波器的数据访问模式。如计算输出y[0],从输入缓冲区x[]读取6个采样点,当第一个访问发生时,cache控制器读取x[0]以及后续地址的若干个数据(取决于cache line的长度),从速度慢的存储器加载一个cache line的数据需要一定的时钟周期的CPU stall。这种加载的一个动机是x[0]后续的数据后面就要被访问到。这个对于FIR滤波器是显然的,因为后面的5个采样点(x[1]-x[5])就要被用到。后面的这5次存储访问就只需要访问高速cache就可以了。

当计算下一个输出y[1]时,5个采样点(x[1]-x[5])就可以重用了,只有一个采样点(x[6])需要重新加载。所有的采样点都在cache内了,访问时不会有CPU stall了,这也就是刚才提到的时间关联性,即上一步利用的数据在下一次处理中还是可能会被用到的。

Cache就是利用数据访问的时间和空间关联性建立的,它让对速度较慢的外存的访问次数尽可能的降低,而让大部分的数据访问都由更高层次的cache存储区来完成。

存储区的速度
Cache系统通常包含以下3级:

  • 第一级(L1)在CPU片内,运行在CPU时钟频率;
  • 第二级(L2)也在片内,但是比L1稍慢,容量较L1大;
  • 第三级 (L3)是外存,最慢容量也最大。

每一层次的cache有不同的数据访问性能,相对的性能比较可以参考下面的表格。

表1. Cache性能,不同级别的存储访问时间比较(a 500 MHz处理器).



3. C64x的两级cache的访问流程

当处理器从存储区请求数据访问时,首先在最高层次的cache内查找,然后再从次高级别的存储区查找。当请求在cache内时就是cache命中,否则是一次cache miss。因而Cache系统的性能将取决于cache命中的比率。对于任意级别的cache,命中率越高性能越好。比如一个内存访问的L1 cache命中率为70%,L2 20%, 其他来自L3,那么以图3所示的性能下,平均一次内存的访问时间为

(0.7 * 4) + (0.2 * 5) + (0.05 * 30) + (0.05 * 220) = 16.30 ns

   

考虑图4所示的TI TMS320C64x DSP的存储架构,两级的片内cache加上片外外存。一级Cache分成程序(L1P)和数据(L1D) cache,每个容量为16 Kbytes。L1缓存数据访问不会有存储stall。L2存储区分成L2 SRAM和L2 cache,无论是哪种配置,L2存储区都需要两个CPU周期完成一次数据访问。不同的DSP,L2的容量不同,如TMS320C6454 DSP,L2的大小为1Mbytes。最后是C64x DSP最大高达2GBytes的外存,外存的访问速度取决于使用的存储器类型,但一般外存的频率在100 MHz左右。图4中的所有的cache(红色)和数据通路都由cache控制器自动维护。


图 4 TMS320C64x Cache结构

Cache的更新
Cache一直是主存的一个拷贝,因而需要cache能随时反映主存的内容。如果数据在cache内被更新,而主存里没有更新,这个cache内的数据就被称为污染(dirty)数据,而数据在主存被更新但是cache内没有更新,这时cache内的数据被称为过时的(stale)数据。

Cache控制器使用一系列的技术来维护cache的一致性。侦听"Snoop"和强制更新"snarf" 是两种常用的技术。侦听是让cache决定主存内的数据的处理影响到被cache的地址的数据。强制更新是把数据从主存拷贝到cache存储器。

Cache通常比主存容量小得多,因而cache最终总会被填满,这时新进入的数据总要代替那些已经在cache内的数据了。有很多种策略决定那些已经在cache内的数据被代替更新如随机代替,先进先出(FIFO)以及最迟不用的策略(LRU),大部分的处理器都采用LRU,即把least-recently-used数据替换为最新的数据。这种策略由于考虑到数据访问的是时间相关性而非常有效。

   

直接映射的cache

Caches要么是直接映射的"direct-mapped",要不就是组相关的"set-associative"。图5是C64x的L1P cache,包含了512个32字节的cache lines。每个外存地址总是映射到同一个cache line,如:

  • 地址 0000h 到 001Fh总是映射到cache line 0
  • 地址 0020h 到 003Fh总是映射到cache line 1
  • 地址 3FE0h 到 3FFFh总是映射到cache line 511.

当开始访问地址4000h,cache容量被完全占用,因而地址4000h 到 401fh又映射到cache line 0.


图 5. 直接映射Caches.

为了保存从外存拷贝的数据信息,每个L1P的cache行包含如下信息:

  • 有效位,表明当前cache line是否包含有效数据;
  • 标签区域,对应于外存地址的高18位,由于每个cache行的数据可以由外存若干地址拷贝而来,如line 0保存可以来自地址0000h 到 001fh的数据也可以来自地址4000h 到 401fh。
  • 组号,对应于地址的5到13 bit;对于直接映射的cache而言,组号对应于cache line号。这个组号对于组相关的cache是非常复杂的。

当CPU开始访问地址0020h时,假设cache已经被完全被设定无效了(invalidated),即没有cache line包含有效数据。此时cache控制器开始根据当前地址的组(即地址的第5到13比特)来看对应的哪个cache line。对于地址0020h来说是cache line 1.然后cache控制器检查line 1的标签位,确认其是否对应于地址0020h到0039h,最后检查有效位,发现其值为0,即该地址的数据并不在cache内,此时cache控制器标记一次cache miss。这次的miss让控制器从外存加载整个cache line(0020h-0039h),同时更新标签tag位,并把有效位设置为1,同时加载的数据传递给CPU,此次数据访问结束。

当还需要继续访问地址0020h时,cache控制器会继续检查组号和标签域,并和存在标签RAM的值比较,同时有效位的值为1,意味着此次是一个cache hit。

组相关Set-associate caches
组级联的cache是直接映射cache的扩展,在直接映射的cache内,每个组只包含一个cache line,而组级联的cache则包含若干个行,被称为"ways."。图6是C64x DSP的L1D cache,一个2-way组级联cache。每个line 64字节,供16Kbytes容量。


图 6. 组关联(Set-associative Caches).

为了保存从外存拷贝的数据信息,每个L1D的cache行包含如下信息:

  • LRU位,指示哪一路是最近最不常用的(least recently used)。
  • Dirty位,表明当前cache line和主存的信息是否匹配;
  • 有效位,表明当前cache line是否包含有效数据;
  • 标签区域,对应于外存地址的高18位。
  • 组号,对应于地址的5到13 bit;

命中和错过与直接映射的cache的规则一样,只不过此时的标签域比较变成比较两路的标签是否和地址的标签一致。如果way 0命中,则取way 0相应的cache line的数据,如果way 1命中,则取way 1相应的cache line的数据。如果两路都miss,则数据需要从外存取,此时LRU比特来决定哪路的相对应的cache line数据被替换。如果LRU位为0则分配在way 0,否则分配在way 1.LRU比特的值随着数据的访问会有所改变,当way 0的cache line被更新,此时LRU比特会被切换成1。需要注意的是,LRU位只有在cache miss的时候才会被查询,但它的状态会在每次cache line被访问时更新,不管这次访问时命中还是miss,是读还是写。

跟L1P一样,L1D也是读分配(read-allocated),即新数据只有在一次读miss时菜更新。当写miss时,是通过write buffer来写入外存,而把L1D旁路了。当写命中时,写入到cache而不是立即更新外存,这是一种回写cache,即CPU改变的数据会在稍后时刻再写回到外存。

污染比特(dirty bit)表明当前cache line的数据被CPU更新但是还没有被写回到外存。开始时污染bit为0,但随着CPU写cache行,相应的dirty位就被置1了,当cache行被从cache清除时,写回到外存后该位又被清零。Cache行被从cache清除,一个可能是因为一个读miss需要更新该cache行,一个原因可能因为用户自己向cache控制器发送了回写指令。

 

优化Cache性能
有三种类型的miss:

  • 强制性的miss(Compulsory miss):即数据被第一次访问时必然存在的miss,这种miss是无法避免的;
  • 冲突的miss(Conflict miss):即cache行没有被继续重用而是被替换了;
  • 容量miss(Capacity miss):即当前cache容量被用完了,容量miss是冲突miss的一种。

对于任何cache miss,在控制器把数据从外存加载到cache都需要一定的CPU stall。为了提高性能,必然让数据尽可能的复用直到被新数据替换。重用该行意味着访问该行内其他位置的数据意味着提高了数据访问的空间相关性。而重用该行也意味着访问的时间相关性的提高,这就是cache优化的基本思想。例如顺序访问内存的时候cache的效率较高,这种访问模式会多次访问同一cache line直到开始下一个cache line的访问。如下面的伪代码

上述代码的性能并不高因为数据访问的跨度较大,这也就意味着当前cache line的数据被重用的可能性降低。

如果一个cache行被清除然后又被访问,那么该行又要被加载到cache line。因而应该尽可能的避免数据在短时间内被清除,应尽可能的让数据复用。能确定造成miss的原因会有效的防止后续的miss。如前所述,容量的miss主要因为cache的容量较小,如果产生capacity miss,最简单的解决方法是增加cache大小,如C64x DSP的L2 cache可以被配置成SRAM或者cache,可以增加更多的L2空间给cache。另外的一个减少capacity miss的方法是减少特定时间内的数据访问量,如果改变数据的循环结构,尽可能的重用后再加载新数据。

如果存在conflict miss,那么重新组织数据以让同时访问的数据映射到不同的set(对于直接映射的cache,组织数据让同时访问数据对应不同的cache line)。改变存储区的布局让数据分配的内存在访问时不会发生冲突。即良好的数据组织会减少cache miss,提高系统性能。

 

优化系统软件提高Cache性能
优化软件以提高cache性能跟平常的针对平坦式记忆体的优化类似,即有效的数据和代码组织。比如合理的组织那些经常执行的函数在一个存储区内,这样组织是为了让DMA有效的把代码从外存拷贝到片内空间。函数的合理组合有利于提高时间和空间关联性。

下面是一些优化cache性能的基本准则,先看一些准则,然后是一些实例。

  • 让函数尽可能充分的对数据处理以提高数据的重用;
  • 组织数据和代码以提高cache命中率;
  • 划分算法来平衡程序cache和数据cache;
  • 组合那些对相对数据进行处理的函数在一个存储区域;

Data reuse数据重用
如前所述,块滤波在一个cache的环境下工作的很好。图1中的FIR滤波就是一个例子。表2是其不同长度的FIR滤波的性能评估benchmark。随着滤波器长度的增加,在一个采样点上的工作量增加,cache的效率也提升了。输入数据为1024点的16-bit数据,FIR长度分别为16到64 taps (T).


表2. 滤波器长度和cache效率

起始,算法的循环buffer为空,分配在L1D,用输入数据从L2空间来填充,一个填充的方法是使用预加载(pre-fetch,pre-load)函数来加载需要用到的数据。表3是使用预加载函数来进行优化得到的cache性能和不预加载的性能比较。


表3. 数据预加载能有效提高cache的效率

数据代码的组织
前面的FIR滤波器的例子里,输入数据是连续的,但是有些函数并不能原生的连续访问数据,连续访问的可能是间隔很远的两个数据,如矩阵乘法和用一个查找表进行数据调整的交织器。输入数据的随机读取,输出数据则是顺序存放。这些乱序的输入数据的读取会造成cache的颠簸,即反复的cache清除和重新访问。因此合理的组织该查找表让读连续而写乱序,因为L1D是read-allocated的,因而不会频繁的更新cache,而写数据可以直接被灌入L2。这种组织方式把cache的效率从60%提高到85%。

避免L1P的冲突conflict misses
前面提到了怎么重新组织数据来提高cache的效率,类似的还有合理的代码放置会提高L1P的性能。改变函数在链接时的次序来调整函数在内存的位置。表4是一个把短时内需要连续调用的函数放置在连续内存的例子。


表4. 一个访问函数的例子

假设函数function_1和function_2在L2空间是交叠的,如图7所示。当调用function_1时,它会被分配到L1P (1)。后续的调用function_2会导致它的代码分配到L1P (2).而这部分的代码映射和function_1有冲突,那么当下一次迭代需要继续读function_1时,就会发生重新把function_1的代码加载到L1P的颠簸。这种形式的cache miss完全可以通过重新安排程序代码在内存的分配排序来避免。


图7. 不合理的代码内存分配会导致L1P cache miss

  

算法分割与函数组合
另外的优化技术是分割算法,一个算法可能会分割成小片让程序代码不能完全放到L1P内或者数据不能完全放到L1D内。一个视频的缩放应用程序提供了代码分割和数据分割的实例。为了提高性能,缩放的时候不是先把整个图像进行水平方向的缩放,然后进行垂直方向的缩放,而是把图像分块,先进行这一块的水平和垂直方向的缩放,再进行后一块的缩放。


图8. 视频缩放函数的数据流程图

每个色彩被分成2个数据缓冲区,以亮度Y分量为例,有BufY0a和BufY0b。buffer内的数据先进行边界扩展, 结果保存到BufY1,然后调用scale_horz进行水平方向的缩放,中间结果保存到RotBufY2,再调用scale_vert进行垂直反向的缩放,结果放在Buf_Y3,最后把各个分量的组合成输出到L2空间。

除了对代码和数据进行分割外,还可以针对相同数据进行处理的算法进行组合到同一片内存区域。例如针对Y分量进行操作的函数放在同一片相邻的内存区域,同样,还可以把Y分量的数据buffer放在临近的数据空间。

通过数据和代码的分割以及函数的组合,你可以把cache的有效率提高到90%以上,即大部分的数据和代码的访问都在cache内完成的。

使用系统优化技术
图9所示的是一个复数和9个输入复数向量的点积比较。前面提到,像点积这种算法是不会重用输入数据的,总是对每个输入样点采取很少的操作,然后就把数据存储起来,这种很少的操作往往需要很少的时间运行,而为了提高数据的重用,应该让一段数据做尽可能多的操作之后再保存到存储空间。如图9中的针对复数向量的简单运算的cache有效率仅为79%,如果采用系统级的优化策略,进行合理的函数功能划分把针对同一块数据的操作组合,cache的有效率能提高到93%.


图9. 复数向量的引用

总结
Caches通过靠近CPU的较快的存储器访问来提高系统带宽提高代码和数据的访问效率。Cache通常容量很小,速度很快,利用数据和代码的访问时间和空间的关联性来让CPU尽可能从cache内存取数据和加载代码,而最近CPU不访问的数据和代码则放在片外的低层次的存储空间。

需要进行对一段数据进行相当大处理运算的算法,如FIR滤波和FFT运算在cache系统里效率很高,而那些迭代运算让我们能获取代码和数据的重用提高cache的有效性。而诸如向量点积的运算不会重用输入数据,运算量很少,可以通过分割代码和数据、重新组织函数代码的组合、数据重用等系统优化策略来提高cache的效率。

References

  1. TMS320C6000 Peripherals Reference Guide
  2. TMS320C6000 DSP Cache User's Guide
  3. Using CacheTune (Code Composer Studio v3.0) to Improve Cache Utilization on TMS320C6000 Targets
  4. http://www.blog.163.com/houh-1984/

关键字:C64x DSP Cache DMA L1P L1D 直接映射 set-associative; 本主题的第一部分主要以TI C64x DSP为例介绍cache缓存的基本概念, 解释了为什么需要cache,cache如何和主内存进行通信以及如何优化cache的性能。第二部分主要介绍了怎么配置cache以及怎样正确的使用cache,即如何保证cache的一致性。其中有关于DMA的传输怎么影响cache以及怎么管理DMA传输的双缓存。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值