コンパイラでは、ループを部分的または完全に展開し、複数のループ反復を並列で実行するようにもできます。これには
pragma HLS unroll
を使用します。ループを展開すると、並列処理が多くなるので、デザインをかなり高速にできますが、ループ反復の演算すべてが並列で実行されるので、ハードウェアをインプリメントするのに多くのプログラマブル ロジック リソースが必要になります。この結果、リソース量が多くなって容量の問題が発生し、カーネル コンパイル処理時間が遅くなってしまうことがあります。そのため、ループ本体が小さいループ、または反復回数の少ないループを展開することをお勧めします。
vadd: for(int i = 0; i < 20; i++) {
#pragma HLS UNROLL
c[i] = a[i] + b[i];
}
上記の例では、ループ本体に pragma HLS UNROLL
が追加されており、コンパイラにループを完全に展開するよう指示しています。データの依存性で許容されれば、ループの 20 回の反復すべてが並列実行されます。
ヒント: ループを完全に展開すると多量のデバイス リソースが使用されますが、部分的に展開するだけの場合、少な目のハードウェア リソースを使用するので、パフォーマンスを向上できることがあります。
部分展開されたループ
ループを完全に展開するには、ループの範囲が定数である必要があります (上記の例では 20)。ただし、範囲が可変のループでも、部分展開は可能です。ループを部分展開するということは、特定数の反復のみが並列実行されるということです。
次のコード例は、部分展開を示しています。
array_sum:for(int i=0;i<4;i++){
#pragma HLS UNROLL factor=2
sum += arr[i];
}
上記の例では、UNROLL
プラグマに係数 2 が指定されています。これは、ループ本体を手動で複製し、その 2 つのループを半分の反復回数分だけ同時に実行するのと同じことです。次のコードは、その例です。この変換により、上記のループの 2 回の反復が並列に実行されます。
array_sum_unrolled:for(int i=0;i<4;i+=2){
// Manual unroll by a factor 2
sum += arr[i];
sum += arr[i+1];
}
ループ内のデータの依存性がパイプライン処理されたループの開始間隔 (II) に影響するのと同様に、展開されたループでも、データの依存性により可能な場合にのみの演算が並列実行されます。ループの 1 回の反復での演算に前の反復からの結果が必要な場合は並列実行できませんが、1 つの反復からのデータが使用可能になるとすぐに次の反復が実行されます。
推奨: まず、
PIPELINE
でループをパイプライン処理をしてから、小型のループ本体を UNROLL
で限られた反復回数分展開して、パフォーマンスを改善していくことをお勧めします。