HLS - 数组优化
- 参考
1. 复杂度
- 程序的两个衡量指标:时间复杂度和空间复杂度。
- 衡量一个算法的时间复杂度和空间复杂度可以使用大O标识法。
- 时间复杂度
- 对于没有循环等复杂结构,时间复杂度为O(1);
- 主要计算何时才可以跳出循环,所以循环结构越复杂的,时间复杂度越大。
- 空间复杂度
- 是对一个算法在运行过程中临时占用存储空间大小的一个量度。
- 对于所需要的临时空间都不随算法处理数据量变化,空间复杂度为1.
- 观察算法在运行过程中是否申请了新的空间,空间的大小为空间复杂度,很多情况下与数组的大小相关。
- 时间和空间复杂度是相互影响的,想要较好的时间复杂度,可能会牺牲空间复杂度,反之一样。
2. Partition展开操作
- 为什么Partition可以提升性能
- HLS将数组映射到Memory中。如果数组是作为顶层函数的形参,那么就会被综合成memory的接口;如果数组在设计内部,会根据数组大小及优化方式被综合成移位寄存器(大小<1024)/BRAM,LUTRAM,UltraRAM(大小>1024)。
- 存在的问题
-
当数组作为存储器来实现时,存储器端口的数量会限制对数据的访问,可能导致流水线失败。
-
假设一数组用单端口RAM实现,功能:扫描数组,依次访问连续三个地址的数据求和;此时每个周期只能进行一次读/写,无法实现流水线。
- 若想实现吞吐量为1,需要预读取数据,并手动对数据访问流水打拍。
dout_t array_mem_perform(din_t mem[N]) { din_t tmp0, tmp1, tmp2; dout_t sum=0; int i; tmp0 = mem[0]; tmp1 = mem[1]; SUM_LOOP:for (i = 2; i < N; i++) { tmp2 = mem[i]; sum += tmp2 + tmp1 + tmp0; tmp0 = tmp1; tmp1 = tmp2; } return sum; }
-
即使使用双端口的RAM,每个时钟周期可进行两次访问,计算一个和,打一拍可以与第三次访问的值再次求和,组成一流水。(我的理解是这样,不太清楚是不是正确的。)
-
- 可以不用更改设计,HLS可以使用优化指令:Array Partition、Array Reshape.
- Array Partition
-
将原先一个大的memory拆分成多个小的memory/registers,增加了存储器的读写端口。提高了设计的吞吐量,但是需要更多的内存/寄存器资源。
-
有3个type可选:block、cyclic和complete
- factor为n,将原数组等分成n个小的数组,每个数组的长度 = 原长度/n。
- 以一维数组为例,如下图
#param HLS ARRAY_PARTITION variable=weight_group block factor = 4 dim = 1 #param HLS ARRAY_PARTITION variable=weight_group cyclic factor = 4 dim = 1 #param HLS ARRAY_PARTITION variable=weight_group complete dim = 1
-
对多维数组采用Partition展开。当dim=0时,数组所有维度都被拆开。
-
3. 数组的映射和重组
3.1 ARRAY_MAP
- 将多个较小的数组映射成一个数组,以减少RAM资源。FPGA中提供的最小RAM单元大小为18KB,如果很多小数组没有很好的使用完整的18K,可以使用MAP指令,将许多小数组映射到一个更大的数组中。
- Horizontal:直接将多个数组拼接在一起,数组的长度等于N+M,数组的宽度为N和M的最大数组宽度。
- Vertical:对于数组的每一个元素进行拼接,数组的长度等于N/M中最大的长度。
- 命令介绍:其中variable是被map的数组;instance是map后的数组;mode可选horizontal/vertical;offset仅horizontal可选,int为合并的数组相较于0的偏移量。
- 注:命令相关例子和用法可以查看Xilinx官网,有较为全面的解释。
#pragma HLS array_map variable=<name> instance=<instance> <mode> offset=<int> //example 将C和D合并成CD #pragma HLS array_map variable=C instance=CD vertical #pragma HLS array_map variable=D instance=CD vertical
3.2 ARRAY_RESHAPE
- 两步:将数组进行Partition + vertical MAP。
- 减少了RAM的数量,组成新的数组,元素更少,但位宽更大,允许在单个时钟周期内访问更多的数据。
#pragma HLS ARRAY_RESHAPE variable=array1 block factor=2 dim=1 #pragma HLS ARRAY_RESHAPE variable=array2 cycle factor=2 dim=1 #pragma HLS ARRAY_RESHAPE variable=array3 complete dim=1