C++, CUDA, Thrust

[C++][CUDA][Thrust]続・単純な二値化処理を4通りの並列化手法で比較してみる

前回、単純な二値化処理を4通りの並列化手法で比較しました。
今回はCUDA関連でもう少し検証してみます。
CUDAのプログラミングで最初に気にするところと言えば、やはりcudaMalloc, cudaFreeのメモリ管理かと思います。出来ることならこの辺りは何も考えなくても勝手にやってくれるようにして欲しいものです。
というわけで今回はNVIDIAが開発している並列アルゴリズムライブラリであるThrustを使って前回の.cuコードを書き直してみました。

参考記事:
An Introduction to the Thrust Parallel Algorithms Library

いくつかとても気になる部分がありましたので、以下、そのメモになります。

書き直したコードは以下の通りです。

cudaMalloc, cudaFreeが不要になります。
最初は元々のコードの最後の部分にあったcudaFreeのところをコメントアウトして、

として

のようにしていたのですが、何故かコンパイル時に

initialization of ‘dev_bit_vertices_vector’ is skipped by ‘goto Error’

というエラーが出てしまいました(この原因は全くわかりませんでした…)。
cudaFreeが必要なくなりましたので、Error:を消してreturnで返すようにしたらエラーは消えたのですが、実行時ではなくコンパイル時にこのエラーが出てしまった理由はわかりませんでした…。

せっかくThrustを使っているので、
thrust::device_vector = std::vectorでのdevice_vectorの初期化
thrust::copyでdevice_vectorからstd::vectorへのデータの書き込み
を行ってみたところ、信じられないくらい速度が遅くなりました…

Thurstを使っているのに毎回raw pointerを取得してcudaMemcpyを使うほうが圧倒的にCPU, GPU間のデータ処理は速いようです。

以下、実行時間の比較です。出力結果に少し後付けで説明を加えています。
もちろん、数字はいじっていません。

CUDA(前回のもの):
Initialize: 170 msec.
GPU Malloc: 99 msec.
CPU -> GPU: 9 msec.
GPU: 0.622 msec.
GPU -> CPU: 9 msec.
Thrust(cudaMemcpy版):
Initialize: 140 msec.
Thrust Malloc: 167 msec.
      + CPU -> GPU
GPU: 0.605 msec.
GPU -> CPU: 52 msec.
Thrust(cudaMemcpy版):
Initialize: 139 msec.
Thrust Malloc: 108 msec.
CPU -> GPU: 9 msec.
GPU: 0.583 msec.
GPU -> CPU: 8 msec.

データ転送時間が6倍近くも変わってしまっていますね…。なんとも残念な結果です。
とは言え、cudaMemcpyを使いさえすればThrust版でも元々のCUDA版と同じパフォーマンスが得られ、且つメモリ解放し忘れを気にしなくて良くなるのはとても大きいですね。
raw pointerを取得すれば既存のカーネル関数も使えるわけですし、さらにThrustの各種アルゴリズムを手軽に使えるのも嬉しい限りです。


※本記事内容は、国立研究開発法人 日本医療研究開発機構(AMED)の平成29年度 「未来医療を実現する医療機器・システム研究開発事業『術中の迅速な判断・決定を支援するための診断支援機器・システム開発』」採択課題である「術前と術中をつなぐスマート手術ガイドソフトウェアの開発」(代表機関名:東京大学、研究開発代表者名:齊藤延人)に、東京大学大学院情報理工学系研究科の学術支援専門職員として参画している瀬尾拡史が、研究開発として行っているものやその成果を一部含んでいます。

コメントを残す

メールアドレスが公開されることはありません。