原创 【博客大赛】cache,你给我听话点

2016-6-16 11:06 1873 11 12 分类: MCU/ 嵌入式
在上一篇“为什么程序越优化越慢?”里,详述了程序指令在cache里发生冲突后另运行效率变的完全不可预测的问题,并提出了两种将不老实的cache变乖的良方。本以为cache已经完全被驯服了,没过多久,已经优化到23s的程序,在我修改了一段代码后,又掉回了30s,调出逻辑分析仪查看主存sram的读取和写入信号线,在运行关键程序段时,cache竟然又不乖了,导致CPU频繁的访问主存,拉低了效率。
 
20130521184506323.jpg
图一
 
不过这次访问的主存地址不在程序指令区,而是数据区域。在编译的map文件里,0x20FE8对应的是一个全局变量guiTime。
 
20130521184530396.jpg
图二
 
guiTime此变量确实有在关键函数里频繁使用到,但是数据cache总共有2KB的空间,而程序所有的全局变量不到1KB。理论上说,运行时,这些全局变量都应该存在数据cache里,不应该去频繁访问主存呀。
 
再看一看程序,guiTime被定义为volatile类型。
 
volatile  uint32  guiTime
 
“为了提高存取速度,编译器会将一些变量存放到一个寄存器内,下次使用的时候直接从寄存器里读取,当原变量被外界(中断,其他线程)改变后,寄存器里的值是不会变的,这样程序里用的寄存器值和实际的变量值就不一致了,错误随之而来。将变量定义成volatile类型,就是为了避免这种错误,每次使用数据都从原始地址读取,而不是寄存器。”
 
上述是对volatile类型一种被广泛认可的解读,注意红色的标注“原始地址”,这是否意味着定义成volatile类型的变量,使用时,都会无视cache,而去访问主存?抱着强烈的好奇心,做了一个小测试,去掉了针对guiTime的volatile定义,测试关键程序的运行时间———还是30s,从逻辑分析仪上仍能看到CPU的频繁访问0x20FE8地址开始的主存。
 
看来不是volatile的问题,至于"每次都要从原始地址读取"里的原始地址到底是哪里,先搁一搁,解决问题要紧。
 
t019b544828e8906521.jpg
 
回头看一看图二,CPU在读完0x20FE8开始的8个32位数据后,立刻又向0x3FFE8开始的地址写入了8个32位数据,而在map文件里,0x3FFE8指向的是程序的stack栈区域,光凭这些暂时还找不到问题的根结。
 
在逻辑分析仪的数据包里,又发现了下面一段波形,图三里,CPU干了两件事,从0x3FFE8地址开始读取8个数据,然后向0x20FE8地址写入8个数据,刚好和图二里干的事情相反。CPU没事在0x20FE8和0x3FFE8两个地址之间导来导去是在干嘛呢? 而且两个地址的末尾都是FE8,一个假象在脑子了蹦了出来-------难道是两个地址在cache里发生冲突了?(10分钟内,不能从"两个地址末尾都是FE8"这条线索联想到cache冲突的同学,请先面壁好好反省cache原理,想不起来的请先阅读前文“为什么程序越优化越慢”)
 
20130521184606161.jpg
图三
 

根据直接映射cache的散列函数:cache_addr = ram_addr  mod  cache_size,0x3FFE8和0x20FE8在2KB的cache里的散列地址都是0x7E8,两个地址上存放的数据肯定会冲突,导致频繁的读取主存,这也解释了为什么很长一段时间没有出现关键函数运行迟缓的现象,因为之前的guiTime的存储地址不在0x20FE8上,没有和栈区数据发生冲突,直到我修改一段代码后,guiTime凑巧被编译器分布在了0x20FE8,结果冲突就发生了。那为什么guiTime变量会和看似毫无关联的栈区数据冲突呢?

回想了下堆栈的作用,恍然大悟。函数调用时,返回地址等一些现场信息都会存入栈中,调用结束后,出栈取出返回地址,PC指针跳转到返回地址继续运行。如果函数被频繁调用,为了减少存取消耗,栈区地址会被直接映射到cache的对应空间,这样函数在调用时,现场信息的存取都将直接在cache里操作,而不再是主存的栈区。但是,如果一个函数的现场信息和要使用到的全局变量,在cache里的映射地址完全相同,悲剧就发生了。
 
假设,Key函数在调用时,现场返回信息先存在cache的0x7F8上,当Key函数在执行过程中要用到一个全局变量guiTime,CPU先到guiTime对应的cache地址0x7F8上寻找,发现没有存储这个变量,于是就从主存0x20FE8读入guiTime,并同时准备将其覆盖到cache的0x7F8上,以备日后使用。但是0x7F8之前存储的是非常重要的函数现场信息,如果这些信息缺失,CPU将不知程序去向——直接跑飞,所以在覆盖之前,CPU会将cache里的现场信息存入主存0x3FFE8开始的真实栈区,到此,已经重现了图二的一幕。函数执行结束后,CPU需要返回地址给以指明一条出路。CPU先到cache的0x7F8上搜寻,寻找无果后,从主存的栈区取回返回地址继续跑路,同时准备将现场信息覆盖到cache的0x7F8上,避免以后重复读取主存,但此时0x7F8上存储的是全局变量guiTime,不能被随意篡改,否则就不能称为"全局变量"了,无奈的解决办法是在覆盖前,将guiTime再写回原始的主存地址,至此,图三的波形也再现了。如果Key函数被频繁调用,图二和图三的波形将频繁交替显现,运行时间就在这交替之间白白的浪费了。
 
解决办法还得从cache的散列函数入手,但是和前文遇到的情况略有不同,栈区的地址一般都从主存的末尾开始,是一个相当大的数值0x3FFFF,和数据所在区域0x20FE8的跨度太大了,远远超过了cache_size的2KB,而且嵌入式系统里也没法提供这么大的cache_size来消除冲突。但是注意,栈区所在区域虽然很大,但是真正使用到的地址是非常有限的,以本文为例,总共使用了0x3FFFF到0x3FFE8的24个地址,对应到cache的映射地址从0x7FF到0x7E8,所以只要数据区域的在cache的映射空间避开这段地址,就可以避免冲突了。前文所述的第二种方法在此就可派上用处了,比如在bsp里,新增一个从0x1000到0x17E7的段.DataCache,然后将程序里的所有的全局变量强制分布到这个段里,这样数据和栈永远也不会冲突了。
 
最后还有一种终极的解决办法就是消除祸根,不用全局变量....
 
再回到文中关于volatile的解读,解决了数据和栈的冲突后,无论在全局变量前是否加volatile的定义,都不会操作主存了。在加入cache的系统后,原始地址的主存已经被映射到了cache上,所以这里“原始地址”的含义其实就是cache了。
 
cache确实是一个不安分的家伙,作风爽快,干事利落,但性格经常会反复无常,很难伺候,对它是又爱又恨,要想用好它,需要好好的持久调教。翻篇后,希望cache能听话点,别再惹事了啊。
 
PARTNER CONTENT

文章评论1条评论)

登录后参与讨论

735618834_912116584 2016-6-15 19:16

写的太好了。前一段时间刚好在调试DSP的cache,预先分配cache太重要了,要不然程序经常会出莫名其妙的问题。

用户898703 2012-4-6 09:18

俄也刚学,多多交流

用户1650588 2012-3-29 11:20

我也是刚刚学,菜鸟一枚,不过我用的是altera的,呵呵

用户377235 2012-3-29 04:39

quartus ii是altera家的

用户1696769 2012-3-26 22:30

社区里有挺多的FPGA高手和研学FPGA的朋友,可以多交流哈
相关推荐阅读
用户419742 2013-05-14 20:57
[博客大赛]程序为什么越优化越慢?
正在开发一个基于Nios II内核的项目,使用的开发环境是nios for eclipse,编译器是GCC,整体功能实现后,开始优化速度。默认没有开启gcc的优化选项,一段关键函数Key的运行...
用户419742 2012-06-13 21:33
再诡异的现象背后可能只是一个傻x的低级错误——谈调试心态
  今天调试一个小模块,FPGA的24号引脚作为输入端,在此引脚上外部给一个恒定的0电平,理论上程序应该一直读为0电平,在开机的前10s,程序内部读取该引脚为0,可是10s后始终读取为1...
用户419742 2012-06-02 20:07
【博客大赛】马克思教我们优化时序之补全if else
  时序优化中重要的一项就是提高模块的最高工作频率,工作频率由关键路径决定,通常的提高工作频率的步骤是:利用时序分析工具找到关键路径,分析关键路径主要延迟是布线延迟还是逻辑延迟,然后再轮番十八...
用户419742 2012-05-24 21:09
【博客大赛】TimeQuest约束外设之诡异的Create Generated Clocks用法
最近在altera FPGA里设计一个外设的驱动模块,模块本身逻辑很简单如下图所示,但是模块和外设之间的时序约束问题搞的很头疼,今天先讲讲总结的一些Timequest下外设约束方法,特别是那毫无用...
用户419742 2012-05-18 20:45
【博客大赛】TimeQuest之delay_fall clock_fall傻傻分不清楚
  这篇我想分享一个之前在用TimeQuest约束双边沿模块的input delay时犯得一个错误,有人看了可能会觉得傻傻的,什么眼神,delay_fall和clk_fall怎么会分不清呢,字...
EE直播间
更多
我要评论
1
11
关闭 站长推荐上一条 /3 下一条