目錄
1. 簡介
2. 代碼解析
2.1 原始代碼
2.2 優化后
2.3 分析優化措施
3. 總結
1. 簡介
在Vitis HLS中,實現II(迭代間隔)= 1是提高循環執行效率的關鍵。II=1意味著每個時鐘周期都可以開始一個新的迭代,這是最理想的情況,可以大大提高硬件執行的并行度和速度。
本文對兩個示例進行分析,針對存在的存儲器訪問瓶頸進行優化,以實現迭代間隔為1的目標。隨后將詳細解析優化后的代碼,探討優化措施對性能的影響。
2. 代碼解析
2.1 原始代碼
存儲器訪問存在瓶頸
#include "ap_int.h"ap_int<10> example(ap_int<7> mem[128]) {ap_int<10> sum = 0;int i;SUM_LOOP:for (i = 2; i < 128; ++i)sum += mem[i] + mem[i - 1] + mem[i - 2];return sum;
}
?綜合報告:
+ Performance & Resource Estimates: PS: '+' for module; 'o' for loop; '*' for dataflow+-------------+------+------+---------+-----------+----------+---------+------+----------+------+----+----------+-----------+-----+| Modules | Issue| | Latency | Latency | Iteration| | Trip | | | | | | || & Loops | Type | Slack| (cycles)| (ns) | Latency | Interval| Count| Pipelined| BRAM | DSP| FF | LUT | URAM|+-------------+------+------+---------+-----------+----------+---------+------+----------+------+----+----------+-----------+-----+|+ example | -| 4.64| 254| 2.540e+03| -| 255| -| no| -| -| 39 (~0%)| 211 (~0%)| -|| o SUM_LOOP | II| 7.30| 252| 2.520e+03| 3| 2| 126| yes| -| -| -| -| -|+-------------+------+------+---------+-----------+----------+---------+------+----------+------+----+----------+-----------+-----+
- 函數總延遲:254 cycles
- 循環總延遲:252 cycles
- 沒有使用 BRAM 或者 URAM,通過 fabric 實現存儲器
此段原始代碼中,每次循環迭代都會從 mem 數組中讀取三次數據(mem[i]、mem[i-1]和mem[i-2]),這些重復的內存訪問會增加訪問延遲。
2.2 優化后
實現II(迭代間隔)= 1。
#include "ap_int.h"ap_int<10> example(ap_int<7> mem[128]) {ap_int<7> tmp0, tmp1, tmp2;ap_int<10> sum = 0;int i;tmp0 = mem[0];tmp1 = mem[1];
SUM_LOOP:for (i = 2; i < 128; i++) {tmp2 = mem[i];sum += tmp2 + tmp1 + tmp0;tmp0 = tmp1;tmp1 = tmp2;}return sum;
}
?綜合報告:
+ Performance & Resource Estimates: PS: '+' for module; 'o' for loop; '*' for dataflow+------------------------------+------+------+---------+-----------+----------+---------+------+----------+------+----+----------+-----------+-----+| Modules | Issue| | Latency | Latency | Iteration| | Trip | | | | | | || & Loops | Type | Slack| (cycles)| (ns) | Latency | Interval| Count| Pipelined| BRAM | DSP| FF | LUT | URAM|+------------------------------+------+------+---------+-----------+----------+---------+------+----------+------+----+----------+-----------+-----+|+ example | -| 3.87| 133| 1.330e+03| -| 134| -| no| -| -| 57 (~0%)| 198 (~0%)| -|| + example_Pipeline_SUM_LOOP | -| 3.87| 129| 1.290e+03| -| 129| -| no| -| -| 37 (~0%)| 139 (~0%)| -|| o SUM_LOOP | -| 7.30| 127| 1.270e+03| 2| 1| 126| yes| -| -| -| -| -|+------------------------------+------+------+---------+-----------+----------+---------+------+----------+------+----+----------+-----------+-----+
- 函數總延遲:133 cycles
- 循環總延遲:129 cycles
- 沒有使用 BRAM 或者 URAM,通過 fabric 實現存儲器
通過引入三個臨時變量tmp0、tmp1、tmp2來減少冗余的內存訪問。這三個變量用于存儲當前及之前兩次迭代訪問的mem數組元素。在每次循環迭代中,只需要讀取一次新的數組元素(tmp2 = mem[i]),然后將這個新讀取的值與之前存儲的兩個值(tmp1和tmp0)相加。然后更新tmp0和tmp1的值,使它們分別變為前一次和當前迭代的值。
在循環內每次只進行一次讀取操作。
2.3 分析優化措施
在對原始代碼進行優化時,核心目標是改善內存訪問模式以提高性能。原始代碼中,每次循環迭代需要進行三次內存訪問,這導致了存儲器訪問成為性能的瓶頸。為了解決這一問題,優化后的代碼引入了臨時變量tmp0、tmp1和tmp2,將前兩次迭代的數據存儲在這些變量中,從而避免了不必要的重復內存訪問。
對于此類問題,可以總結出以下優化原則:
減少內存訪問: 通過引入臨時變量,優化后的代碼顯著減少了對存儲器的訪問次數。現在,每次迭代只需進行一次內存訪問,大大降低了訪問延遲,提高了數據訪問效率。
優化數據重用: 引入臨時變量tmp0、tmp1和tmp2使得前兩次迭代的數據得以重復利用。這種數據重用策略增強了數據訪問的局部性,減少了存儲器的頻繁訪問,有助于進一步提高性能。
改進迭代間隔: 優化后的代碼不僅減少了存儲器訪問次數,還改善了內存訪問模式,使得循環執行效率得到了顯著提高。這種改進有助于實現迭代間隔II=1,即每個時鐘周期開始一個新的迭代,從而進一步提高了整體代碼的性能表現。
3. 總結
通過對原始代碼進行優化,成功地改善了內存訪問模式,提高了循環執行效率。優化后的代碼減少了存儲器訪問次數,優化了數據重用,以及改進了迭代間隔,使得每個時鐘周期都可以開始一個新的迭代。這些優化措施有效地減少了存儲器訪問延遲,提高了硬件執行效率。優化后的代碼在性能和效率上都有了顯著的提升,更適用于高性能處理應用場景。