[[ASAP Top]] #contents ---- * Multi-GPU system with ExpEther (Express system) [#r3e8633e] ExpEtherという仮想化技術により、ホスト(CPU)と複数のデバイス(GPU)を10Gbps Ethernetで接続するシステムである。 ExpEtherではPCIeのパケットをEthernetのフレームにカプセル化するということを行っている。 そのため、このシステムのユーザからはホストのマザーボードに直接GPUが挿さっているように見える。 GPU-BOXはこれのプロトタイプで、8個のExpEther NICと3000Wの電源が付いた箱のこと。 最大8台のGPUを搭載することができる。 このGPU-BOXは、2013年度卒の野村先輩(通称のむさん)から脈々と実機評価@NECで使われていた。 しかし、2015年2月から市販品のExpEther I/O拡張ユニット (N8000-1005) が使われるようになる。 基本的には入れ物が変わっただけで、ExpEther NICに変化は無いため、性能に差はない。 このシステムの売りは主に拡張性にある。 ExpEther I/O拡張ユニットをEthernet switchにつなぐだけでGPUの増設が簡単に行えるからである。 「この拡張性が既存のデータセンタに光をもたらす」とか云々言って論文のIntroductionが始まるのである。 ** 実機環境 [#ydb6ae69] - 2015/02/{02,09} |CPU|Intel Xeon E5-1650 @ 3.20GHz| |Host Memory|16GB| |OS|CentOS 6.3| |CUDA|Toolkit 5.5| |GPU|NVIDIA Tesla K20 ×4| |ExpEther board|NEC N8007-104| |I/O expansion box|NEC N8000-1005 ×2| |Switch|Mellanox SX1012 ×2| |Network|10Gb Ethernet ×2| ** これまでにやってきたこと [#i69a1697] - のむさん -- 主に通信関連のマイクロベンチマークを取る -- GPU-BOXが複数ノード構成のマルチGPUシステムよりも優れていることを示す - 三石 -- CUDA版Linpackで評価を取る --- 意外と通信しまくっていたため性能が悪かった --- 結局excelにまとめただけでお蔵入り -- Graph500を用いてBFSの高速化 --- 通信量の削減と通信の隠蔽 ** 一般的なシングルホストマルチGPUシステム (Normal system) との違い [#s03e4407] ここでいうNormal systemとは、1つのマザーボードのPCIeスロットに複数のGPUが付いているシステムのことを指す。 基本的にNormal systemでできることはExpress systemでもできる。 つまり、Normal systemのコードがそのままExpress systemでも利用でき、正しく動くということ。 ただし、利用できるからといって実際に使い物になるとは限らない。 PCIeの帯域幅と10Gbps Ethernetの帯域幅に大きな差があるためである。 PCIeの世代にもよるが、だいたい"10Gbps Ethernetの帯域幅 = 1/5 x PCIeの帯域幅"くらいの差がある。 - 10Gbps Ethernetの帯域幅 = 1.25GB/sec (10Gbps) - PCIeの帯域幅 = 6GB/sec (50Gbps) このことを考慮していないコードが性能的に使い物にならないということは容易に想像がつくだろう。 ** Express systemを使うときの注意事項 [#ye4d86b6] とりあえずここで言いたいことは、実機評価@NECでは油断するな、ということ。 *** どのGPUが外付けなの? [#g09687e8] Express system@NECのホストのPCIeスロットには、 ExpEther NICの他にディスプレイ出力用のNVIDIA製GPUが1つ挿さっている。 上述したが、ユーザからは外付けGPUもホストマシンに内蔵しているように見えてしまうため、 ユーザには外付けGPUsと内臓GPUが見えることになる。 試しにnvidia-smiを実行すると以下のようになる。 この環境では、Tesla C2050がホストに直挿しされていて、他のTesla K20はすべて外付けされている。 $ nvidia-smi #ref(nvidia-smi.txt) よく見ると、GPU名の左横になにやらIDのようなものが振ってあることがわかる。 これを見て、「今回はTesla C2050は使わないから、GPU 0以外を使わないようにしよう!」と考えてしまいがちだが、これは罠である。 GPU IDはdeviceQueryで確認しなければならない。 $ /usr/local/cuda/samples/1_Utilities/deviceQuery/deviceQuery #ref(deviceQuery.txt) deviceQueryにより、実際は次のようにGPU IDが振られていることが確認できる。 - GPU 0: Tesla K20 - GPU 1: Tesla C2050 - GPU 2: Tesla K20 - GPU 3: Tesla K20 - GPU 4: Tesla K20 どうやらNVIDIAは、シングルGPU実行時では自動的に最新(もしくは最高性能)のGPUを優先的に使うようにしているらしい。 以上により、外付けのGPUは0, 2, 3, 4番であるとわかった。 *** どうやってGPUを指定するの? [#ke4890e0] "どのGPUが外付けなの?"の環境を仮定して、サンプルを提示する。 基本的に以下のような関数をプログラムの冒頭で1回だけ行えば良い。 以下で行われるCUDA runtime関数は意外と重たいので注意。 static int id_table[] = { 0, 2, 3, 4 }; // 使いたいGPU IDを列挙する void InitializeDevices(int num_gpus) { for (int i = 0; i < num_gpus; ++i) { for (int j = 0; j < num_gpus; ++j) { int gpu_id = id_table[i]; int peer_id = id_table[j]; if (gpu_id == peer_id) continue; cudaSetDevice(gpu_id); int can_access_peer = 0; cudaDeviceCanAccessPeer(&can_access_peer, gpu_id, peer_id); if (can_access_peer == 0) continue; printf("Enabling peer access to GPU %d from GPU %d\n", peer_id, gpu_id); cudaDeviceEnablePeerAccess(peer_id, 0); } } } - id_table: この配列に使いたいGPUのIDを列挙する - cudaSetDevice: 「今からgpu_id番のGPUを使いますよ」という合図 - cudaDeviceCanAccessPeer: gpu_id番のGPUとpeer_id番のGPUがpeer accessできるかどうか調べ、結果をcan_access_peerに書き込む - cudaDeviceEnablePeerAccess: セットされているGPUとpeer_id番のGPUのpeer accessを許可する *** カーネル内で他GPUのglobal memoryへ直接アクセスしないほうが良い [#na82e775] - アクセスがwarp単位でコマ目に行われ、そのたびにEthernetフレームのヘッダが付加される -- warp単位のアクセス量 = 32 x sizeof(Type) = 最大256Byte -- Ethernetフレームには46〜1500Byteのデータが格納可能(46Byte以下のデータはパディング) - 計算と通信のオーバラップが難しい *** pinned memoryは使わないほうが良い [#uf6bc0fa] - pinned memory: GPUからも直接見ることができるホストメモリ領域 -- pinned memoryへのアクセス = ホストとデバイスとの通信 - 帯域の狭いネットワークを通ってアクセスするのでスループットがでない - 複数のGPUが同時に1つのホストにアクセスしようとすると衝突が起こる *** 実機@NECでプログラムが正しく動かない時 [#yf8a38b3] - 基本的に同期 cudaDeviceSynchronize がうまくとれていないことが考えられる -- Normal systemで開発を行っていると起こりがちな問題 -- Express systemはNormal systemの約5倍の通信レイテンシであることに注意 -- 通信後はちゃんと各GPUで同期 cudaDeviceSynchronize をやるべし *** 評価はX Windowを立ち上げずにやったほうがいい [#e0e6734a] - GUIの描画にGPUのメモリが使われるため - なんだか色んなマシントラブルを引き起こすような気がする *** プログラム実行中にフリーズする [#mdd49b99] - ExpEtherは熱に弱いらしく、熱くなると固まる ** 覚えておくと便利なコマンド [#e1ebe920] - lspci -tvv: デバイスの確認 - nvidia-smi: デバイスの確認 - /usr/local/cuda/samples/1_Utilities/deviceQuery/deviceQuery: GPU IDの確認 - ifconfig: 実機評価@NECでデータのやり取りを行うのに使う $ ifconfig eth16 down $ ifconfig eth16 192.168.0.3 $ ifconfig eth16 up ** 参考 [#xa3de05f] - [[PCI Express wikipedia:http://ja.wikipedia.org/wiki/PCI_Express]] - [[パソコンの森:PCI-Expressについて:http://pcnomori.blog.fc2.com/blog-entry-116.html]] written by mits(2015-02-14)