优化等级越高编程时间反而增加?
SmartPro 6000F使用全FPGA架构,并内嵌了4颗Nios II软核,我们使用的开发环境是Nios for Eclipse,编译器是GCC。在帮客户定制一款NAND Flash编程时序时,为了进一步提高编程速度,准备对时序进行优化。默认情况下,GCC的优化等级为最低的O0,编程时间为30s,开启O1一级优化后,编程时间降到了25s,当开启O2二级优化后,编程时间没有继续下降,反而又上升到了29s。
猜想造成乌龙的原因
虽然不同优化等级之间时间差别就不到5s,我们完全可以很省事的开启O1级别优化时序,然后就交付给客户,但是当时我们并没有这么做。因为理论上,优化等级越高,编程时间应该越少才对,但是现在测试的编程时间结果是 O0 > O2 > O1,冥冥中感觉时序还可以再优化些,速度还可再快一点。
目前提速遇到的问题是:使用更高的优化等级,编程时间反而更多了,这可不符合GCC的优化规律啊。那是什么破坏了优化规律呢?宏观的考虑,在嵌入式系统里,能破坏程序运行规律的家伙,嫌疑最大的就是Cache了。
Cache存在的初衷是为了提速,因为程序指令如果完全运行在内存里,速度会非常慢,而在Cache里运行将非常快,但是Cache的容量是有限的,无法缓存所有程序,所以Nios II内核在设计的时候做了一个折中处理,先将内存里的程序搬运到Cache里,然后在Cache里运行程序,由于Cache无法一次性缓存所有程序,如果运行的程序大小超过了Cache容量,必须要重新访问内存更新Cache,如果更新频率越高,则访问内存的次数就越多,运行效率自然就会被拉低,而Cache的更新频率是不可预测的,所以配置了Cache的嵌入式系统的运行时间一般都很难预测。
但是,SmartPro 6000F里的每一个Nios II内核都配置了4KB的指令Cache和2KB的数据Cache,而编程时序只有不到2KB,理论上完全可以缓存到Cache里运行,根本不用去更新Cache,也就不应该存在运行不规律的问题了。
实际测试验证猜想
上述只是我们的一个理论分析,实际运行到底有没有更新Cache,还需实际测试说了算。我们将6000F的内存SRAM的读(rd)、写(wr)和地址(addr)连上逻辑分析仪观察,如果rd线或者wr线有出现脉冲,就说明存在访问内存更新Cache的操作。不同的优化等级下,测试波形如下所示:
看来Cache的加载方式好像没有之前想的那么简单,为了解决这个疑惑,让我们再来研究下在O2优化等级时编程时序的汇编代码。编程函数Program在内存SRAM的地址分布区域从0x1014到0x15F0,里面会调用到的一个定时器函数Timer在内存的分布从0x2020到0x2170。
到这里,所有问题似乎都豁然开朗了,不同等级的优化设置后,在改变函数大小的同时,也会改变它们在内存的地址分布。Program和Timer的分布地址,通过Cache的散列后如果没有冲突,那么在运行时就不会访问内存,如果产生了冲突,并且冲突的地址越多,则访问内存的时间就会越长,整体速度就会越慢。O2的优化等级比O1高,虽然前者优化后的程序更小,但是前者在Cache的散列加载地址发生了冲突,速度自然就更慢了。
我们如何解决这个问题?
要解决冲突问题,必须从Cache的散列函数入手。
方法一:增大cache的容量
由于Program和Timer的函数分布地址跨度过大,超过了cache_size,才导致散列后发生冲突,如果将cache_size增大到8KB,Program在Cache里的加载地址是0x1014,Timer在Cache里的加载地址是0x20,不会发生冲突。但是嵌入式系统里的资源都非常精贵,很多系统无法提供这么大的Cache,此时可以采用另一种更实惠的方法。
方法二:通过分散加载,将Program和Timer的分布地址跨度缩小到cache_size内
在BSP(板级支持包)里,新增一个从0x1000到0x2000的段.UserCache,然后将Program和Timer强制分布到这个段里,这样两个函数在Cache里的存储地址也不会冲突了。GCC里,将函数到分布到指定的段的语法如下:
以前一直听说加入Cache后,程序的运行就会变的不可预测,这次算是彻底的感受到了,但不可预测并不代表不可控,通过上述的两种方法,就可以控制函数尽量不去访问内存,提高执行效率和运行的一致性。
好景不长,发展总是螺旋上升的
但是好景不长,在给客户增加了一个小功能后,Cache又犯毛病了,不过这次出问题的不是指令Cache,而是数据Cache,详文请期待“毫秒必争之如何搞定Cache(下)”。
了解更多关于SmartPro 6000F的信息请登陆网址:
http://www.zlg.cn/sitecn/program/cate_62.html
联系客服