Multi-GPU system with ExpEther Multi-GPU system with ExpEther/注意事項 †とりあえずここで言いたいことは、実機評価@NECでは油断するな、ということ。 どのGPUが外付けなの? †Express system@NECのホストのPCIeスロットには、 ExpEther NICの他にディスプレイ出力用のNVIDIA製GPUが1つ挿さっている。 ユーザからは外付けGPUもホストマシンに内蔵しているように見えてしまうため、 ユーザには外付けGPUsと内臓GPUが見えることになる。 試しにnvidia-smiを実行すると以下のようになる。 この環境では、Tesla C2050がホストに直挿しされていて、他のTesla K20はすべて外付けされている。 $ nvidia-smi よく見ると、GPU名の左横になにやらIDのようなものが振ってあることがわかる。 これを見て、「Tesla C2050は使わないから、GPU 0以外を使うようにしよう!」と考えてしまいがちだが、これは罠である。 GPU IDはdeviceQueryで確認しなければならない。 $ /usr/local/cuda/samples/1_Utilities/deviceQuery/deviceQuery deviceQueryにより、実際は次のようにGPU IDが振られていることが確認できる。
どうやらNVIDIAは、シングルGPU実行時では自動的に最新(もしくは最高性能)のGPUを優先的に使うようにしているらしい。 以上により、外付けのGPUは0, 2, 3, 4番であるとわかった。 どうやってGPUを指定するの? †"どの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); } } }
上記のやり方だけでなく、設定ファイルみたいのを作ってそれをプログラムに食わせたりすると楽かも。 カーネル内で他GPUのglobal memoryへ直接アクセスしないほうが良い †
pinned memoryは使わないほうが良い †
実機@NECでプログラムが正しく動かない時 †
プログラム実行中にフリーズする †
外付けGPUを使うプログラムの実行がやたらと遅い場合がある †
written by mits(2015-02-14) |