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
    

posted @ 2023-06-14 21:50  可达达鸭  阅读(318)  评论(0编辑  收藏  举报