インテル®高位合成 (HLS) コンパイラー プロ・エディション: ベスト・プラクティス・ガイド

ID 683152
日付 12/16/2019
Public
ドキュメント目次

4.2.3. 例 : ループのパイプライン処理および展開

ここで検討するデザインでは、マトリックスの各カラムとマトリックスの他の各カラムとのドット積を行い、6つの結果を異なる上三角のマトリックスに格納します。マトリックスの残りの要素はゼロに設定する必要があります。



コードは次のコード例のようになります。
1.	#define ROWS 4
2.	#define COLS 4
3.	
4.	component void dut(...) {
5.		float a_matrix[COLS][ROWS]; // store in column-major format
6.		float r_matrix[ROWS][COLS]; // store in row-major format
7.	
8.		// setup...
9.	
10.		for (int i = 0; i < COLS; i++) {
11.			for (int j = i + 1; j < COLS; j++) {
12.	
13.				float dotProduct = 0;
14.				for (int mRow = 0; mRow < ROWS; mRow++) {
15.					dotProduct += a_matrix[i][mRow] * a_matrix[j][mRow];
16.				}
17.				r_matrix[i][j] = dotProduct;
18.			}
19.		}
20.	 	
21.	 // continue...
22.		
23.	}

このコンポーネントのパフォーマンスを向上させるには、特定のカラムの各エントリーで繰り返されるループを展開します。ループ動作が独立している場合、コンパイラーでは、それを並列実行します。

浮動小数点演算を実行する順序は、通常、ソースコードで表現されているのと同じにして、数値精度を維持してください。ただし、--ffp-contract=fast コンパイラー・フラグを使用して、浮動小数点演算の順序を緩和することができます。浮動小数点演算の順序を緩和すると、このループ内のすべての乗算が並行して発生する場合があります。詳細については、次のチュートリアルを確認してください。 <quartus_installdir>/hls/examples/ tutorials/best_practices/ floating_point_ops

コンパイラーでは、展開することでパフォーマンスが向上すると考えられる場合、ループを単独で展開しようとします。例えば、14行目のループは自動展開されます。これは、ループのイタレーション回数は一定であり、多くのハードウェアを消費しないためです。(ROWS は、コンパイル時に定義された定数です。これにより、このループのイタレーション回数が一定になります。)

スループットを向上させるには、11行目で j ループを展開します。ただし、コンパイラーによるループの展開を許可するには、必ず、コンパイラーに定数の境界を持たせてください。定数の境界を持たせるには、j ループを j = i + 1 ではなく、j = 0 で始めます。また、 プレディケーション・ステートメントを追加して、r_matrix の無効なデータによる割り当てが j ループのイタレーション 0,1,2,… i の間に行われないようにしてください。
01: #define ROWS 4
02: #define COLS 4
03: 
04: component void dut(...) {
05: 	float a_matrix[COLS][ROWS]; // store in column-major format
06: 	float r_matrix[ROWS][COLS]; // store in row-major format
07: 
08: 	// setup...
09: 
10: 	for (int i = 0; i < COLS; i++) {
11: 
12: #pragma unroll
13: 			for (int j = 0; j < COLS; j++) {
14: 				float dotProduct = 0;
15: 
16: #pragma unroll

17: 				for (int mRow = 0; mRow < ROWS; mRow++) {
18: 					dotProduct += a_matrix[i][mRow] * a_matrix[j][mRow];
19: 				}
20: 
21: 				r_matrix[i][j] = (j > i) ? dotProduct : 0; // predication
22: 			}
23: 		}
24:  	}
25:  	
26: 	// continue...
27: 	
28: }

これで j ループが完全に展開されました。依存関係がないため、4イタレーションすべてが同時に実行されます。

詳しくは、resource_sharing_filter チュートリアルを参照してください。このチュートリアルの場所は次のとおりです。 <quartus_installdir>/hls/examples/tutorials/best_practices/resource_sharing_filter

続行して、ループの展開を10行目ですることもできますが、このループを展開すると、面積が再び増加します。コンパイラーで、このループを展開するのではなく、パイプライン処理できるようにすると、面積の増加が回避でき、さらに使うのは約4クロックサイクルのみになります ( i ループのIIが1のみの場合)。ハイレベルのデザインレポートのLoops Analysis (report.html ) の詳細ペインには、この改善方法のヒントが表示されます。

次に挙げる要因は、通常、ループIIに影響を与えます。
  • ループ搬送依存関係

    <quartus_installdir>/hls/examples/tutorials/best_practices/loop_memory_dependency にあるチュートリアルを参照してください。

  • 長いクリティカル・ループ・パス
  • ループII > 1の内側のループ