おさらい

先日開催されたHalideによる画像処理プログラミング入門では、2次元畳み込み(Convlution)演算のHalide による実践的な記述例が解説されました。このセミナーではハードウェアとして Intel Core i7-5930K (3.5GHz, 6Core. 12/03日現在でAmazon.com で$649.99 のミドルレンジDesktop CPU)をターゲットに、幾つかの最適化テクニックが紹介されました。

最終的には、もとの教科書的なC++コードと比較して、

  1. Halide で実装するだけで18.4倍
  2. Y次元で4 iteration 分のループ展開(unroll) でさらに1.14倍
  3. Y次元でのスレッド並列化(parallel) でさらに6.10倍

の高速化達成し、2048×2048, 8bit グレースケール画像の場合に、処理時間は0.52msec となりました(リファレンスのC++コードでは66.4msec)。

当サイトHalide2FPGA の目玉のひとつはHalide からFPGA IP を出力することにあります。では、この2次元畳み込みコードをFPGAで実行させた場合、どのぐらいの処理速度となるか試してみましょう。

 

FPGAでの処理速度

まず、FPGA向けにスケジューリングを一部変更する必要があります。残念ながら、Halide には、ターゲットのハードウェアを理解して最適なスケジュールングを自動でやってくれるような機能はありません。最適なスケジューリングは自分で見つけなければなりません。FPGA向けのスケジューリングと聞くと、x86やGPUに比べて面倒なイメージを持たれるかもしれませんが、Genesis コンパイラ(Fixstars 製Halide to FPGAコンパイラ)を前提にした場合ではFPGAスケジューリングのHalide 記述はむしろシンプルになります。

Xilinx のVivado HLS を知っている方であれば、Vivado HLS に#pragma unroll や#pragma pipeline などの最適化オプションが存在することをご存知と思います。それらの最適化オプションはHalide に明示的には出てきません。Genesis コンパイラが、ループのサイズやストライド、扱うアルゴリズムなどから総合判定して自動で適用されます。

FPGAのコードとしては、こちらのProject にあるコードをベースにしました。Intel コードとの処理時間の比較のために、

  1. kernel サイズを3×3
  2. 変数は16ビットの固定少数点数ではなく、単精度浮動小数点(float)
  3. 画像X方向に連続したピクセルの同時処理数(unroll_factor) を4

に変更しています。ターゲットのハードウェアを、Avnet Zedboard (Xilinx Zynq XC7Z020-CLG484-1) にして、ビルドを行うと、処理時間の見積もりは以下のようになりました (GenesisコンパイラはバックエンドにXilinx HLS を使っています。こちらはHLSコンパイラから出力されるレポートです)。

================================================================
== Performance Estimates
================================================================
+ Timing (ns): 
    * Summary: 
    +--------+-------+----------+------------+
    |  Clock | Target| Estimated| Uncertainty|
    +--------+-------+----------+------------+
    |ap_clk  |   6.66|      7.41|        0.83|
    +--------+-------+----------+------------+

+ Latency (clock cycles): 
    * Summary: 
    +---------+---------+---------+---------+----------+
    |      Latency      |      Interval     | Pipeline |
    |   min   |   max   |   min   |   max   |   Type   |
    +---------+---------+---------+---------+----------+
    |  2097258|  2097258|  1049195|  1049195| dataflow |
    +---------+---------+---------+---------+----------+

 

レポートの見方ですが、クロックタイミング(ap_clk. 逆数が動作周波数です)が6.66 nsec で、処理にかかるLatency (クロック数) が2097258 ということなので、最初のピクセルがFPGAの処理のパイプラインに入ってから、最後のピクセルがパイプラインから出てくるまでの処理時間は、6.66 nsec * 2097258 = 14.0 msec となります。Xilinx Zynq XC7Z020は、Zynq シリーズの中でもコストパフォーマンスと低消費電力を特徴とした組み込み向けチップです。ミドルレンジ・デスクトップ用のCPU であるIntel Core i7 と単純に処理速度を比較しては、流石に分が悪いでしょう。参考までに、両プロセッサの処理速度の差は14.0 / 0.52 = 27 倍になります(下図左のグラフ)。この図では縦軸は処理時間ではなく、その逆数、つまり一秒間当たりの処理回数を”Performance” と定義して示しています。つまりHigher is bettter になります。

さて、消費電力にシビアな組み込む向けチップではしばしば、処理能力の絶対値ではなく、単位電力あたりの処理能力を重要な指標にしています。”Performance per Watt” という言葉を聞いたことがある方も多いでしょう。処理能力をそのチップの消費電力で割った値です。この値が大きいほど、より処理効率がよいというわけです。では、このPerformance per Watt を計算してみましょう。

まずIntel Core i7 ですが、こちらのデータシートにあるTDW(Thermal Design Power) を使います。シートによれば、TDW は140 Wです。流石に大きいですね。次に、Zynq ですが、組み込む向けということもあり、様々な省電力機能やテクニックがあるため、なかなか典型的な値がみつかりません。ここでは、Plogramable Logic(PL: FPGA側)を目一杯使った場合の値を消費電力としましょう。動作周波数200MHz, 使用率が100%の場合でも、PL の消費電力は1.5W 程度です。結果として、Pefromance をWattで割ったのが下図の右のグラフになります。”Performance per Watt” という指標では、逆にZynq の方が3.5倍ほどCore i7 よりよい値となりました。

Performance Comparison

 

リソース消費量

FPGAの場合、FPGA上のリソースをどれだけ消費するかも、FPGA IP品質の重要な指標の一つになります。同じくXilinx HLS から出力されるレポートは以下のようになりました。

================================================================
== Utilization Estimates
================================================================
* Summary: 
+-----------------+---------+-------+--------+-------+
|       Name      | BRAM_18K| DSP48E|   FF   |  LUT  |
+-----------------+---------+-------+--------+-------+
|DSP              |        -|      -|       -|      -|
|Expression       |        -|      -|      44|     50|
|FIFO             |        0|      -|       0|     12|
|Instance         |        2|    180|   35504|  44569|
|Memory           |        -|      -|       -|      -|
|Multiplexer      |        -|      -|       -|     36|
|Register         |        -|      -|       6|      -|
+-----------------+---------+-------+--------+-------+
|Total            |        2|    180|   35554|  44667|
+-----------------+---------+-------+--------+-------+
|Available        |      280|    220|  106400|  53200|
+-----------------+---------+-------+--------+-------+
|Utilization (%)  |    ~0   |     81|      33|     83|
+-----------------+---------+-------+--------+-------+

 

単一のConvolution アプリケーションにもかかわらず、DSP(Digital Signal Processor) とLUT(Look Up Table) を81%も使用してしまっています。Zynq FPGAでは、IEEE準拠の浮動小数点演算についてのコストが高いので、C/C++のリファレンスコードでの単精度浮動小数点(float) をそのまま持ち込むとこのような結果になりがちです。FPGAの採用を検討するのであれば、アルゴリズムの精度に関する調査を行い、float の代わりに固定小数点の使用が可能がどうか検討するべきです。

固定小数点を使うと、浮動小数点演算に比べてFPGA上で消費するリソース数が少なくなります。結果として他のロジックを追加したり、さらなる最適化を適用してスループットを上げることができます。

 

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedIn

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です