如何优化使用C6000系列C64x的Cache--原理,Cache种类和优化策略
2014-11-15 09:28
260 查看
本主题的第一部分主要以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. 存储访问定位的规律
图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结构
图 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).
上述代码的性能并不高因为数据访问的跨度较大,这也就意味着当前cache line的数据被重用的可能性降低。
表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. 复数向量的引用
|
相关文章推荐
- 何优化使用C6000系列C64x的Cache--原理,Cache种类和优化策略
- TensorFlow学习系列(五):如何使用队列和多线程优化输入管道
- 如何优化加载大位图(BitmapFactory使用系列)
- MySQL优化系列(三)--索引的使用、原理和设计优化
- MySQL优化系列(三)--索引的使用、原理和设计优化
- 如何优化加载大位图(BitmapFactory使用系列)
- TensorFlow学习系列(五):如何使用队列和多线程优化输入管道
- MySQL5.7性能优化系列(二)——SQL语句优化(4)——使用合并、物化策略优化派生表、视图引用
- MySQL5.7性能优化系列(二)——SQL语句优化(3)——使用物化策略优化子查询
- 如何使用 @ OutputCache 指令的 VaryByCustom 属性来缓存不同版本的页面
- 如何使用dameware远程控制windows vista business 64 bits? 推荐
- LockWindowUpdate系列2:LockWindowUpdate是打算如何使用的?
- C++Builder 2007系列1-如何使用TDD For C/C++
- 如何使用dameware远程控制windows vista business 64 bits?
- 如何在ASP.NET2.0下面使用基于数据库(SqlServer2000)的Cache
- BizTalk学习笔记系列之二:实例说明如何使用BizTalk
- 使用临界段实现优化的进程间同步对象-原理和实现
- SqlServer版用户如何使用全文索引,进行优化网站搜索
- [MySQL优化案例]系列 -- 无法使用查询缓存
- 如何提升在使用DevExpress 系列DataGrid的性能问题