メモリ アーキテクチャの最適化 - 2020.1 Japanese

Vitis 統合ソフトウェア プラットフォームの資料: アプリケーション アクセラレーション開発 (UG1393)

Document ID
UG1393
Release Date
2020-08-20
Version
2020.1 Japanese

メモリ アーキテクチャはインプリメンテーションの重要な側面です。帯域幅のアクセスには制限があり、次の図に示すように全体的なパフォーマンスに大きく影響することがあります。


void run (ap_uint<16> in[256][4],
          ap_uint<16> out[256]
         ) {
  ...
  ap_uint<16> inMem[256][4];
  ap_uint<16> outMem[256];

  ... Preprocess input to local memory
  
  for( int j=0; j<256; j++) {
    #pragma HLS PIPELINE OFF
    ap_uint<16> sum = 0;
    for( int i = 0; i<4; i++) {

      sum += inMem[j][i];
    }
    outMem[j] = sum;
  } 

  ... Postprocess write local memory to output
}

このコードでは、2 次元入力配列の内部次元に関連する 4 つの値が追加されます。これ以上変更をしないでインプリメントすると、次のような見積もりになります。

図 1. パフォーマンス見積もり

全体的なレイテンシが 4608 (Loop 2) なのは、18 サイクル (内部ループ 16 サイクル + 合計のリセット + 書き出される出力) が 256 回反復されているからです。これは、HLS プロジェクトのスケジュール ビューアーで確認できます。見積もりは、内部ループを展開すると大幅に改善されます。

図 2. パフォーマンス見積もり

ただし、このように改善されるのは、主にプロセスがデュアル ポート メモリの両方のポートを使用しているからです。これは、プロジェクトのスケジュール ビューアーから確認できます。

図 3. スケジュール ビューアー

メモリからのすべての値にアクセスして合計を計算するために、2 つの読み出しがサイクルごとに実行されています。これにより、メモリへのアクセスが完全にブロックされてしまうので、望ましくない結果になることがあります。結果をさらに改善するには、2 番目の次元を使用してメモリを 4 つの小型メモリに分割します。

#pragma HLS ARRAY_PARTITION variable=inMem complete dim=2

詳細は、 pragma HLS array_partition を参照してください。

これにより、4 つの配列読み出しになり、すべてが 1 つのポートを使用して異なるメモリで実行されます。

図 4. 4 つの配列の実行結果

Loop 2 に合計 256 x 4 サイクル = 1024 サイクルが使用されます。

図 5. パフォーマンス見積もり

または、メモリを 4 ワードの 1 つのメモリに再形成します。これには、次のプラグマを使用します。

#pragma HLS array_reshape variable=inMem complete dim=2

詳細は、 pragma HLS array_reshape を参照してください。

これにより、配列分割と同じレイテンシになりますが、この場合は 1 つのポートを使用した 1 つのメモリになります。

図 6. レイテンシ結果

どちらのソリューションでも全体的なレイテンシおよび使用量は同じようになりますが、配列を再形成した方がインターフェイスがきれいで、配線の密集は少なくなります。

注記: これで配列最適化は終了です。実際のデザインでは、ループを並列処理するとレイテンシがさらに改善できることがあります (ループの並列処理 を参照)。
void run (ap_uint<16> in[256][4],
	  ap_uint<16> out[256]
	  ) {
  ...

  ap_uint<16> inMem[256][4];
  ap_uint<16> outMem[256];
  #pragma HLS array_reshape variable=inMem complete dim=2
  
  ... Preprocess input to local memory
  
  for( int j=0; j<256; j++) {
    #pragma HLS PIPELINE OFF
    ap_uint<16> sum = 0;
    for( int i = 0; i<4; i++) {
      #pragma HLS UNROLL
      sum += inMem[j][i];
    }
    outMem[j] = sum;
  } 

  ... Postprocess write local memory to output

}