【再掲】【研究課題レポート抜粋】GPGPUによるパーソナルスーパーコンピュータの可能性 | サイバーエージェント 公式エンジニアブログ
※このエントリは、Watanabe氏が優秀賞を受賞した第4回(2010年3月)研究課題レポートからの抜粋です。

 はじめに
 現在のCPUはムーアの法則と呼ばれる経験則の通り処理能力を上げてきたが、トランジスタの集積率の限界、発熱問題などにより限界に達しつつある。
 そのため各ベンダーはIntel Coreシリーズ、AMD Athlon64などマルチコア化の道へ踏み出している。

 その流れに対して、GPUは座標変換や光源処理などに使う比較的単純な演算を大量に行うことでグラフィックス性能を今もなお大幅に上げ続けている。
 単純な演算であるが故に演算処理をソフトウェアで処理せずに、ハードウェア化し高速化を図っていることや、ハードウェアによるメニーコアによる処理が可能なことがその下支えとなっている。

 GPGPUは、そのメニーコア技術を汎用的に利用できるようにする仕組み「(General Purpose Computation on Graphics Processing Unit)汎用GPU」の略である。
 GPUに搭載されているメニーコアによる演算能力をグラフィックス以外にも利用する技術として近年注目を集めている。

 長崎大学ではGPUを760個並列し158TFLOPSの演算性能をたたき出し、地球シミュレータの122TFLOPSを上回る性能を出した。
 ちなみに発表された開発費は3800万円である。(※地球シミュレータは400億円)

 NVIDIA CUDA
 NVIDIAが提供するGPUを利用した並列コンピューティングアーキテクチャ。

    機能・特徴
  • GPUで並列アプリケーション開発を行うための標準C言語

  • FFT(高速フーリエ変換)およびBLAS(線形代数の基本サブルーチン)用標準数値ライブラリ

  • GPUとCPU間での高速データ転送パスを使用したコンピューティング専用CUDAドライバ

  • OpenGLおよびDirectXグラフィックスドライバとCUDAドライバを同時使用可能

  • Linux 32/64ビット、Windows XP 32/64ビット、およびMacのOSをサポート

アーキテクチャ
サイバーエージェント 公式エンジニアブログ-研究課題レポート

 実行されるthreadはBlockという単位で束ねられ、それぞれのBlockは共有のメモリを持つ(Shared Memory)。Blockを束ねるものとしてGridという概念があるが、GPUカーネル上で動作するGridは一つだけである。

 Blockは最大で65536個生成可能、1Blockあたりの最大スレッド数は512となっている。
 ※ハードウェア的にはBlockに対応するProcessor(StreamingMultiprocessor)とthreadに対応するProcessor(ScalarProcessor)の搭載数が違うので、実行順に各Pocessorに割り当てられる。

 各threadの生成コストは非常に少ない(1クロック)ので、ソフトウェアで生成するスレッドのようにあまり生成コストをあまり気にかける必要はない。

 メモリはthreadローカルメモリ→SharedMemory→GlobalMemoryの順でレイテンシが大きくなるので注意が必要。
 基本的にread/write共に1clockで完了するが、GlobalMemoryに関しては400〰600クロックのレイテンシが加わる。


 GPUとCPUの性能比較



演算装置製品クロック周波数core数TDPメモリクロックメモリバンド幅
CPUCorei7(-975 Extreme Edition)3.3GHz8(HT含む)130W266MHz(DDR3-2133の場合)17.067GB/sec
GPUGeForce9800 GT1.35GHz112105W900MHz57.6GB/sec
GPU(Felmi)Tesla20701.40GHz512225W2048MHz170GB/sec

 GPUは、CPUに比べてクロック周波数が低いものの、core数が文字通り桁違いに多く搭載されていることが分かる。また、メモリ性能についても3倍~10倍の差がある。
 ※FelmiはNVIDIAの次世代アーキテクチャ。

 開発環境
  • CUDAドライバ
  • CUDAツールキット一式
  • CUDA SDKコードサンプル
がNVIDIAからダウンロード可能。
 SDKには、以下の内容が含まれる。
  • nvcc Cコンパイラ
  • GPUのためのCUDA FFT及びBLASライブラリ
  • プロファイラ
  • GPUのためのgdbデバッガ
  • CUDAランダムドライバ(標準NVIDIA GPUドライバでも使用可能)
  • CUDAプログラミングマニュアル


 コンパイル及びオプション
 C言語で記載されたソースコード(cuda酔うには、拡張子.cuを使う)をnvccを使ってコンパイラを行う。

 よく使うオプションは、
  • --output-file (-o)

  •  ファイルの出力先を指定する。
  • --pre-include (-include)

  •  includeファイルの指定を行う。CUDA用のユーティリティとしてcutilが提供されているので、そのヘッダーファイル(cutil.h)の指定を行う。
  • --library (-l)

  •  ライブラリの指定を行う。
  • --device-debug (-G)

  •  デバッグ時は、このオプションをつけてコンパイルする必要がある。
  • --device-emulation (-deviceemu)

  •  GPUが搭載されていない環境で実行する際は、エミュレーションモードで起動する必要があるので、このオプションをつける。


例:
nvcc -g -G -o /home/hoge/cudaDev/affineConvertOnCuda
 -include /home/hoge/NVIDIA_GPU_Computing_SDK/C/common/inc/cutil.h
 /home/hoge/cudaDev/affineConvertOnCuda.cu


 デバッグ
 cuda-gdbを利用する。
 基本的な使い方はGDBを踏襲するが以下の拡張機能がある。
  • デバイス(GPU)メモリ上の変数を参照可能
  • スレッド間の切り替え可能(thread<<<(BX,BY),(TX,TY,TZ)>>>)
  • CUDA上のブロック数、スレッド数を参照可能(info cuda threads)
上記拡張機能により、スレッドを切り替えて、ステップ実行というような細かいデバッグが可能である。
[yuhsaku@localhost cudaDev]$ cuda-gdb affineConvertOnCuda  ←cuda-gdbをプログラム指定で起動
(cuda-gdb) break affineConvertOnCuda.cu :73  ←73行目にブレイクポイント
(cuda-gdb) run  ←実行
[Current CUDA Thread<<<(0,0),(0,0,0)>>>]  ←現在のスレッドが表示される
Breakpoint 1, convert () at /home/yuhsaku/cudaDev/affineConvertOnCuda.cu:73
73 to_matrix_p[to_counter]=matrix_p[counter];  ←73行目のコードが表示される
Current language: auto; currently c++

(cuda-gdb) print counter  ←現在のスレッドの変数counterを表示する
$1=0

(cuda-gdb) info cuda threads  ←現在の全スレッドを確認する
<<<(0,0),(0,0,0)>>>...<<<(15,0),(23,0,0)>>>convert ()
 at /home/yuhsaku/cudaDev/affineConvertOnCuda.cu:73

(cuda-gdb) thread<<<(1,0),(1,0,0)>>>  ←ブロック1の中のスレッド1に切り替える
(cuda-gdb) print counter  ←切り替えたスレッドの変数counterを表示する
$1=1

(cuda-gdb) cont  ←次のブレイクポイントまで実行する


 環境構築
Linux
OS: CentOS 5.4
  1. cudadriver_2.3_linux_64_190.18.run
  2. cudatoolkit_2.3_linux_64_rhel5.3.run
  3. cudasdk_2.3_linux.run
の順番でインストール。
ホームディレクトリにNVIDIA_GPU_Computing_SDKというディレクトリが作成される。
  • libXmu-devel.x86_64
  • libXi-devel.x86_64
が必要になるので、インストールする。

MacOS
  1. drivers/cudadriver_2.3.1a_macos.pkg
  2. toolkit/cudatoolkit_2.3a_macos_32.pkg
  3. gpucomputingsdk_2.3a_macos_32.pkg
の順番でインストール。
/Developer/GPU Computing というディレクトリにインストールされる。

※ Linux, MACともに以下の環境変数をセットする。
C_INCLUDE_PATH="/Developer/GPU Computing/C/common/inc"
 :/usr/local/cuda/include:$C_INCLUDE_PATH
LIBRARY_PATH="/Developer/GPU Computing/C/common/lib":"/Developer/GPU Computing/C/lib"
 :/usr/local/lib:$LIBRARY_PATH


 環境の確認
 deviceQueryを実行し、CUDAが利用可能か確認する。
※GPUが対応していない、認識されない場合は、以下のDeviceの箇所が
Device 0: "Device Emulation (CPU)"
と表示される。

以下に、主要な情報について記載する。
[yuhsaku@localhost release]$ ./deviceQuery
CUDA Device Query (Runtime API) version (CUDART static linking)
There is 1 device supporting CUDA

Device 0: "GeForce 9800 GT"
 CUDA Driver Version:            2.30
 CUDA Runtime Version:            2.30
 CUDA Capability Major revision number:   1
 CUDA Capability Minor revision number:   1
 Total amount of global memory:       1073020928 bytes  ←グローバルメモリ
 Number of multiprocessors:         14
 Number of cores:              112  ←コア数
 Total amount of constant memory:      65536 bytes
 Total amount of shared memory per block:  16384 bytes  ←ブロック別のsharedメモリ
 Total number of registers available per block: 8192
 Warp size:                 32
 Maximum number of threads per block:    512
 Maximum sizes of each dimension of a block: 512 x 512 x 64
 Maximum sizes of each dimension of a grid: 65535 x 65535 x 1
 Maximum memory pitch:            262144 bytes
 Texture alignment:             256 bytes
 Clock rate:                 1.35 GHz  ←クロック
 Concurrent copy and execution:       Yes
 Run time limit on kernels:         No
 Integrated:                 No
 Support host page-locked memory mapping:  No
 Compute mode:                Default (multiple host threads can use this device simultaneously)


 演算パフォーマンス
 行列同士を積算させるプログラムをコンパイルして実行を行い、検証する。

環境:CentOS 5.4, Corei3, mem4G, GeForce 9800 GT
※CPUでの処理はシングルスレッドのため、1コアしか利用していない。

512*512の行列同士の積算
処理方法計算時間(sec)計算結果
CUDA(GPU)0.00528502464294433593750000000017614889811968.000000
CPU0.98808908462524414062500000000017614889811968.000000

1024*1024の行列同士の積算
処理方法計算時間(sec)計算結果
CUDA(GPU)0.032414913177490234375000000000563314824314880.000000
CPU32.760878086090087890625000000000563314824314880.000000

2048*2048の行列同士の積算
処理方法計算時間(sec)計算結果
CUDA(GPU)0.25409007072448730468750000000018020237517520896.000000
CPU279.38737201690673828125000000000018020239665004544.000000

上記実行結果から、今回利用しているGPUの場合は、100倍~200倍の性能が出ていることが分かる。

 考察
 今回は、NVIDIAから提供されているCUDAを利用した。
 今後は、ATIから提供されているATI Streamなどもあることから、統合フレームワークのOpenCLを利用した開発が効率的かと思われる。

 基本的には演算処理を文字通り桁違いに速くできる(並列化できる)ので、大量の計算処理が必要なR&D向きかと思われる。
 実際、NVIDIA(CUDA)の採用事例としても物理シミュレーションから石油・ガス探査、製品設計、医療用画像などが挙げられている。

 しかし、今回の検証で得られたことは、
  • 並列処理を意識したプログラミングスキルを習得することによって個人でも新たな可能性を生み出すことができる。
  • 「演算のみ速くできても、(フロントエンドとしての)Webサービスでは利用シーンがない」ではなく「常識を覆すほどの演算能力を手に入れることにより、常識を覆すWebサービスを作ることができる可能性がある」ということを意識できる。
という点になる。

 プログラマは、ハードウエアのスケールアップに依存した逐一的なプログラミングから、ハードウエアの性能を完全に引き出し、スケールする並列プログラミングの技術を習得することが重要になると予測する。

 1コアあたりの性能限界を迎えつつある今、プログラマの技術転換の時代が来ているのかもしれない。

 参考文献
【1】Cellの最大演算性能 http://todotani.cocolog-nifty.com/blog/2008/02/cell_1b22.html
【2】Cuda ZONE http://www.nvidia.co.jp/object/cuda_home_new_jp.html
【3】CUDA-GDB http://developer.download.nvidia.com/compute/cuda/2_1/cudagdb/CUDA_GDB_User_Manual.pdf
【4】GPUコンピューティングの現状 http://www.slideshare.net/pfi/20091210-gpu
【5】マルチコアCPUでプログラミング言語が変わる? http://pc.nikkeibp.co.jp/article/trend/20080528/1003620/