zoukankan      html  css  js  c++  java
  • Vivado_HLS 学习笔记4-嵌套的循环的优化

    优化的原理

    HLS会自动尝试最小化循环的延迟. 除了这些自动的优化之外,directive文件负责

    • 执行并行任务; 例如相同函数的多次执行,以及相同循环的多次迭代. 要进行pipeline设计;
    • 重新设计数组(Block arrays),函数,循环和端口等的物理实现,改善数据的访存;
    • 提供数据依赖的信息;

    最终的优化手段是修改C源代码以移除非必要的数据依赖.

    参考ug871的第7章.

    对有数据依赖的计算如何优化设计

    void matrixmul(
          mat_a_t a[MAT_A_ROWS][MAT_A_COLS],
          mat_b_t b[MAT_B_ROWS][MAT_B_COLS],
          result_t res[MAT_A_ROWS][MAT_B_COLS])
    {
      // Iterate over the rows of the A matrix
       Row: for(int i = 0; i < MAT_A_ROWS; i++) {
          // Iterate over the columns of the B matrix
          Col: for(int j = 0; j < MAT_B_COLS; j++) {
             res[i][j] = 0;
             // Do the inner product of a row of A and col of B, 
             // 显然,内积的计算中,每次计算都需要读取上一次的res[i][j]并更新本次的res[i][j].存在同一循环不同迭代间的数据依赖.
             Product: for(int k = 0; k < MAT_B_ROWS; k++) {
                res[i][j] += a[i][k] * b[k][j];
             }
          }
       }
    
    }
    

    解决步骤1-pipeline最底层loop,

    • 操作: 设置留空II以initiaion interval 为1
    • 结果: 当最底层的loop循环进行pipeline不能降低latency(运算一次数据需要的时钟周期数)和Interval(下一次输入数据需要等待的间隔,如果等于latency说明没有pipeline)时,查看console看一下是否有蓝色字体 **Unable to enforce a carried dependence constraint **,执行步骤2;

    解决步骤2-pipeline上一层loop

    • 操作: 选择其上一层loop进行pipeline,而不是最底层pipeline.
    • 如果发现并没有获得想要的结果,再次查看console的warnning!,发现Unable to schedule 'load' operation ('a_load_1', matrixmul.cpp:60) on array 'a' due to limited memory ports. Please consider using a memory core with more ports or partitioning the array 'a'.;

    解决步骤3-数组分块(并行,需要多个端口并行)或reshape(需要一个更宽的端口)

    • 操作1: 本例中,需要将数组a的[k]维度也就是第2维进行展开,数组 a -> Directive -> ARRAY_RESHAPE, dimension:2
    • 操作2: 本例中,需要对数组b的[k]维度也就是第1维进行展开;数组 a -> Directive -> ARRAY_RESHAPE, dimension:1
    • 结果: 能够看到II已经是1,满足了流水的设计要求;

    解决步骤4-尝试添加FIFO接口

    • 操作: 对输入端口a,b,和输出端口res设置-> Directive -> Interface ,ap_fifo
    • 结果: console显示错误Port 'res' (matrixmul.cpp:48) of function 'matrixmul' cannot be set to a FIFO [SYNCHK 200-91] as it has both write (matrixmul.cpp:60:13) and read (matrixmul.cpp:60:13) operations.
    • 分析: 在57行和60行等,对res[0][0]进行了多次连续的写操作,不满足stream的状况.这不是stream而是random access.
    • 结果: 这种情况不能使用FIFO!

    解决步骤5-继续优化!修改C代码

    在重写C代码之前,需要对数组a,b,的访存地址进行分析:

    • 为了实现序列的流访存,端口只能在图中红色高亮的部分访存;
    • 对于数组a,b的其他蓝色的读数据口,需要使用内部缓存cached获取;
    • 对于输出res的其他端口,只能通过临时变量进行缓存,最终在红色时输出.

    因此,改写后的代码为:

    void matrixmul(
          mat_a_t a[MAT_A_ROWS][MAT_A_COLS],
          mat_b_t b[MAT_B_ROWS][MAT_B_COLS],
          result_t res[MAT_A_ROWS][MAT_B_COLS])
    {
    #pragma HLS ARRAY_RESHAPE variable=b complete dim=1
    #pragma HLS ARRAY_RESHAPE variable=a complete dim=2
    #pragma HLS INTERFACE ap_fifo port=a
    #pragma HLS INTERFACE ap_fifo port=b
    #pragma HLS INTERFACE ap_fifo port=res
      mat_a_t a_row[MAT_A_ROWS];
      mat_b_t b_copy[MAT_B_ROWS][MAT_B_COLS];
      int tmp = 0;
    
      // Iterate over the rowa of the A matrix
      Row: for(int i = 0; i < MAT_A_ROWS; i++) {
        // Iterate over the columns of the B matrix
        Col: for(int j = 0; j < MAT_B_COLS; j++) {
    #pragma HLS PIPELINE rewind
          // Do the inner product of a row of A and col of B
          tmp=0;
          // Cache each row (so it's only read once per function)
          if (j == 0)
            Cache_Row: for(int k = 0; k < MAT_A_ROWS; k++)
              a_row[k] = a[i][k];
          
           // Cache all cols (so they are only read once per function)
         if (i == 0)
                Cache_Col: for(int k = 0; k < MAT_B_ROWS; k++)
                   b_copy[k][j] = b[k][j];
    
          Product: for(int k = 0; k < MAT_B_ROWS; k++) {
            tmp += a_row[k] * b_copy[k][j];
          }
          res[i][j] = tmp;
        }
      }
    }
    

    注意事项

    如果没有pipling loops,那么每计算一个数字,需要interval,计算tripcount次,就需要tripcount * interval;
    pipling loops把循环的latency从$$Latency = iteration latency * tripcount $$ 变成了 $$Latency = iteration latency + (tripcount * interval)$$.

    • loop的latency是指循环执行完成一次需要的时钟数;
    • iteration latency是指循环内部单次执行完一次需要的时钟数;
    • tripcount是指循环执行的次数.
    • initiation interval是指本次数据输入和下次数据输入需要的间隔周期数.

    注意: 对上层循环/模块的pipeline将导致对所有模块均进行pipeling,将展开所有循环.会极大地消耗资源.获得更高的吞吐量.

  • 相关阅读:
    程序员必知的8大排序(四)-------归并排序,基数排序(java实现)
    程序员必知的8大排序(三)-------冒泡排序,快速排序(java实现)
    程序员必知的8大排序(二)-------简单选择排序,堆排序(java实现)
    程序员必知的8大排序(一)-------直接插入排序,希尔排序(java实现)
    算法的时间复杂度概念
    时间复杂度的计算
    mysql索引总结(3)-MySQL聚簇索引和非聚簇索引
    mysql索引总结(2)-MySQL聚簇索引和非聚簇索引
    【笔试题】2020华为笔试
    C++中的break、continue、goto语句
  • 原文地址:https://www.cnblogs.com/charleechan/p/13186569.html
Copyright © 2011-2022 走看看