5.4. 消除循环的低效率
-
代码移动:将要执行多次但是计算结果不会改变的计算,移动到到循环外面。
image.png
5.5. 减少过程的调用
不判断数组边界减少过程调用,对比如下:
image.png
就像stl中vector的成员函数和下标运算符at(),[],at进行范围检查,而[]不进行范围检查。
5.6. 消除不必要的内存引用
消除前:
image.png
消除后:
image.png
由于优化前每次都要读两次内存,写一次内存,减少到读一次内存。主要是减少了读写内存的次数。
小结
对于以上三次优化,达到的效果为:
image.png
原始代码
image.png
消除循环的低效率
image.png
减少过程的调用
image.png
消除不必要的引用
可见消除不必要的引用能够使得优化的效果提升最明显。
5.7.理解现代处理器
image.png
处理器能够达到的最大优化目标
image.png
已经达到的优化与目标值的对比:
image.png
5.8. 循环的展开
循环展开是达到延迟界限的手段:
image.png
循环展开后的效果如下;
image.png
展开次数与CPE的关系:
image.png
进一步优化的瓶颈:
由于连续相乘或者相加等操作需要下次运算使用上一次的结果,使得两个动作必须是顺序化的。这使得处理器无法使用流水线化的功能特性。 造成了最低只能达到延迟的界限。为了突破延迟界限,需要使得连续相乘也能并行化。
5.9. 提高并行性(利用流水线的特性)
5.9.1多个累计变量
类似于cuda中的延迟隐藏特性。使得更多的数据就位,充分利用流水线特性。
为了将相乘解偶,使用多个并联相乘值:
image.png
这使得我们突破了延迟界限:
image.png
继续增加多路并ixng行:
image.png
都几乎达到了吞吐量的界限。
需要展开的数量
此数量与延迟L与容量C有关,即。则根据下图:
image.png
取各个的最大值,k需要值为。从上图可以看出,在等于10时,乘法达到吞吐量界限。
5.9.2.重新结合变换
重新结合变换也是打破了连续相乘的依赖性:
image.png
image.png
关键路径:
image.png
与combine5的对比:
image.png
结果少了一次。
5.9.3 使用向量化的并行:
以上都是标量的修改,使用向量,可以继续提高:
image.png
再次小结
可以总结优化的步骤如下:
- 消除循环的效率(1.x倍的提升);
- 减少过程的调用(很少的优化);
- 消除不必要的内存引用(减少访问内存,多用寄存器)(x倍的优化);
- 循环的展开,(1.x倍的优化),使得达到了延迟界限;
- 使用流水线的特性(两种方式)达到延迟界限 (x倍的优化);
- 使用向量并行;
- 注意重新的结合可能影响浮点数的准确性
可以看出对于优化,最有效的是3、5项的优化,即消除不必要的内存引用与流水线特性提高并行性。
5.11. 一些限制因素
寄存器溢出:
当并行都p超出了可用寄存器数量时,效率反而下降:
image.png
但是一般不会出现,在寄存器溢出之前就达到了吞吐量界限。
--2021.04.04于清明节假期第二天
狗子上天了
狗子上树了
放风筝时狗子风筝上树了,为了救它,我也跟着爬上去了