一. 基本性能指标
for循环优化的基本概念、对for循环施行流水的优化、for循环的展开以及for循环的循环变量的数据类型是否对结果资源有影响
1. 流水线优化
2. for循环的展开
- 默认情况下for循环是被折叠的,所谓折叠可以理解为所有每次循环都是采用同一套电路,只是这个电路被分时复用,而展开就意味着这个for循环被复制了n或者n/2份,这个是可以设置的;
- for循环可以部分展开,比如trip count为6时,可以将for循环拆分成3个,分别对应(0,3)、(1,4)、(2,5)次循环,其中同组的共用一套逻辑资源;
3. 循环变量i
正常情况下,循环变量i的类型不会影响最后的综合结果,因为Vivado HLS考虑的是i的最大值,然后使用FPGA的最小资源
4. 总结
二. for循环优化——循环合并
- 两个相互独立的for循环我们期望可以并行执行,但是在默认情况下,HLS是顺序执行的,我们可以在HLS中通过directive来将两个for循环合并;
- 合并可以帮助我们在一定程度上降低latency,这是因为在默认情况下for循环都会创建额外的状态机,而状态机会占用额外的时钟周期和资源;
- 当循环边界不一致时,以较大的那个作为合并后的循环边界;
- 如果一个循环变量是边界,而另一个是变量,这个时候不能合并,会报错;
- 如果两个循环边界都是变量,同样合并时会报错;
对于变量作为边界的for循环如何合并?
合并的规则:
- 循环边界都是常数且不一致时,合并后的循环边界由较大的边界决定;
- 边界都是变量时,要求两个变量能够达到的最大值是一致的,这保证了它们有相同的迭代周期;
总结:
三. for循环的优化——数据流
循环之间有依赖关系,可以应用流水线,不可以用合并;
没有应用数据流时循环执行顺序为左,应用了之后为右,可以看出来,使用了数据流之后我们并不需要前面的循环完全执行结束后才执行下一个循环任务,只有前面的有输出我就可以做下一个子任务了。同时各个任务之间有交叠,这帮助我们降低latency进而提高数据吞吐率。
dataflow优化的限制:
- single-producer-consumer Model,两个循环同时使用前面的同一个变量,不能应用数据流,改善的方法是在前面将这个变量分别赋给不同的两个变量,进而这两个变量分别进入不同的循环;
- Bypassing Tasks Model,temp3的数据来自loop2,所以不能使用合并和dataflow,改善的方法是在中间循环中通过简单的复制消除bypass;
- Configuring Dataflow Memory Channels,HLS实现不同任务之间的channels是通过ping-pong或者FIFO缓冲器;如果参数类型为scalar、pointer、reference,HLS会将其综合成FIFO,如果是个数组,HLS判断出数据流是顺序的就会配置成FIFO,否则就是ping-pong RAM;
- 也可以使用cofig_dataflow configuration显式地告诉HLS要配置成FIFO还是ping-pong RAM,配置成FIFO时要特别注意FIFO的深度,否则在C和RTL协同仿真时会报错;
总结:
四. for循环优化——嵌套的for循环
三种类型的嵌套for循环:
- Perfect loop nest:循环的边界都是常量,同时循环体只会在最内层的for循环中出现;
- Semi-Perfect loop nest:最外层循环的边界是变量,但是最内层for循环边界是常数,同时循环体一定在最内层for循环;
- Imperfect loop nest有两种,一种是循环边界都是常量,但是外部循环也有循环体,第二种是虽然外部循环没有循环体,但是内部循环的边界是变量;
对于Imperfect loop,我们希望通过一些手段将其转化成Perfect或者Semi-Perfect loop。
对某层for循环做流水,该层下面的for循环都会被展开;
不加任何约束、对最内部做流水、中间层做流水、最外部做流水的比较:显然对最外部做流水的延迟最小,但对应的增加了硬件成本;同时对整个函数做流水会将所有层都展开,延迟最低,硬件成本最高。
矩阵乘法优化:
总结:
五. for循环优化的其他方法
1. 并行化的问题
2. Loop Pipeline with Rewind Option
rewind并不是适合所有for循环,它是有条件的。
什么时候使用Pipeline会失效?
当将一个任务做流水时,for循环下面的操作都会被展开,如果for循环边界是变量时,这个操作就会失败;
3. 如何处理循环边界是变量的情形?
当循环边界是变量时,HLS无法确定loop的latency,进而无法确定整个函数的latency。
对于这种情形有三种解决方法:
- 使用Tripcont这种directive,这种方法不会对综合结果有任何影响,只是用于report的显示以及不同solution之间的比较;
- 将循环边界的数据类型声明为 ap_int,对综合结果有优化;
- 在C代码中使用assert macro;
三种方法的比较:可以看出来,使用assert的方式latency最小,硬件成本也最低。
总结: