計画の歴史--ネットワーク篇

目次

MPI test
TCP library
TCP library/GAMMA 再び?

ハードウェアは一応安定したと信じてネットワーク設定・チューニングに入る。

2002/4/8 MPI test

既に GbE NIC (Planex GE1000TC) がついて Linux で普通に動いているので、 別に何も問題はなかろうと思って前に書いた MPI send/receive でピンポンす るプログラムを動かしてみる。

と、、、なんとピークで 8MB/s しかでない。で、前に元平木研の下見淳一郎様(どうもありがとうございます)からいただいた TCP/IP ソケットを生に使うプログラムで測ると 40 MB/s でる。

なにが悪いのかと NIC を Netgear GA620T に換えると、 TCP/IP 78MB/s, MPI 43 MB/s という素晴らしい性能(しかし MPI の性能低下が大きい、、、)がで る。台坂君が FreeBSD で MPICH を動かした時には GN1000T で30MB/s くらい はでているので、 software の問題であることは間違いない。

2002/4/9 TCP library

問題は対応をどうするかである。 ぱっと思いつくのは

  1. GA620T をそろえる
  2. 他の NIC をなんとかする
  3. GN1000T で MPICH が遅い理由を解明する
  4. GN1000T で TCP/IP ベースで通信する
といったところであろう。時間的なことを考えるとおそらく最後の選択枝が一番速い。とにかく MPICH を使うとどんなカードでもかなり遅くなるので、代替案があること自体は悪くないしね。

というわけで、ソケットで通信というのをやってみることにする。といっても そんなことはやったことがないので、とりあえず下見様のプログラムを見て何 をすればいいのか考える、というか単にやってることをコピーする。

どうも、 TCP/IP での通信には「クライアント」と「サーバー」があるらしい ということがわかる。これは一度繋がれば別になにか非対称性があるわけではなく、つながるまでの操作の違いらしい。

クライアントの側がすることは

  sock = socket (AF_INET, SOCK_STREAM, 0);
  
  if (sock < 0)  {
      perror_and_exit ("socket");
  }
  
  if (connect (sock, (struct sockaddr*) &sai, sizeof (sai)) < 0) {
      perror_and_exit ("connect");
  }
  
  return sock;
}
みたいに、まず socket を呼んで、次に connect を呼ぶ。

サーバ側は

  s = socket (AF_INET, SOCK_STREAM, 0);
  setsockopt (s, SOL_SOCKET, SO_REUSEADDR, 
		  (void*) &val, sizeof (val));
  bind (s, (struct sockaddr*) &sai, sizeof (sai));
  listen (s, SOMAXCONN);
  cs = accept (s, (struct sockaddr*) &csai, &len);
という具合になんだか難しいことをする。

原理は、サーバ側は名前(ホスト名とポート番号)によって指定された名前で手 を開いて接続要求があるのを待つ。で、クライアント側はその名前で connect を要求する。両方がうまく手をつなげると、 connect と accept のそれぞれ が抜けて戻ってくるということらしい。

複数の相手とつなぐのはどうすればいいかというと、クライアント側はもちろ ん上の処理を繰り返すだけである。サーバ側は listen までやったところで待 ち状態にははいっていて、ここで複数の要求を受付られる。 accept を何回か よべばそれが適当な順にでてくるらしい。

データのやりとりは MPI を使うより実は簡単で、送るほうは send, 受け取る ほうは recv system call を使うだけ。どちらも、転送途中で勝手に帰ってく ることがあるみたいなので残りをちゃんと転送するループを書く必要があるけ ど、まあそれだけ。

MPI で既につながっているもの同士の接続を考える。

接続するルーチンと通信するルーチンは分離する。これは、接続についてはちょっ とややこしい問題があるから。プロセッサ 0, 1 が同時にプロセッサ 2 に接 続要求して、 2 は 0 からくるのを待つといった状況が起きると処理が面倒な ので、そういう競合は起きないということをアプリケーション側で保証するこ とにする。 具体的には MPI_barrier とかでなんとかする。

API としては、以下の2つの関数を準備することにする。

int tcp_request_connection_by_MPIname(int othermpiid,
                                      int direction)
direction は 0 ならサーバ、1なら(非 0 なら) クライアント。 リターンコードは 0 なら成功、 それ以外なら失敗。
int tcp_transfer_data_by_MPIname(int othermpiid,
                             int direction,
                             int length,
                             void* message_buffer)
direction は 0 なら受け取り、1なら(非 0 なら)送り出し。

と、とりあえずこんなところか。これで実装してみよう。

というわけで実装してみた。一応3ノードとかでも通信はできる。速度は、 GN1000TC の場合に



received size  = 1 count = 2000
size, count = 1 2000 wall clock time = 0.848137  0.037730 MB/s
size, count = 1 2000 wall clock time = 0.800074  0.039996 MB/s
received size  = 2 count = 2000
size, count = 2 2000 wall clock time = 0.863618  0.074107 MB/s
size, count = 2 2000 wall clock time = 0.799940  0.080006 MB/s
received size  = 4 count = 2000
size, count = 4 2000 wall clock time = 0.882713  0.145007 MB/s
size, count = 4 2000 wall clock time = 0.800093  0.159981 MB/s
received size  = 8 count = 2000
size, count = 8 2000 wall clock time = 0.844426  0.303165 MB/s
size, count = 8 2000 wall clock time = 0.800074  0.319970 MB/s
received size  = 16 count = 2000
size, count = 16 2000 wall clock time = 1.176345  0.435246 MB/s
size, count = 16 2000 wall clock time = 0.799951  0.640039 MB/s
received size  = 32 count = 2000
size, count = 32 2000 wall clock time = 1.417380  0.722460 MB/s
size, count = 32 2000 wall clock time = 0.799979  1.280034 MB/s
received size  = 64 count = 2000
size, count = 64 2000 wall clock time = 1.562601  1.310635 MB/s
size, count = 64 2000 wall clock time = 0.799963  2.560118 MB/s
received size  = 128 count = 2000
size, count = 128 2000 wall clock time = 1.288446  3.179023 MB/s
size, count = 128 2000 wall clock time = 0.800653  5.115824 MB/s
received size  = 256 count = 2000
size, count = 256 2000 wall clock time = 1.633381  5.015364 MB/s
size, count = 256 2000 wall clock time = 1.512344  5.416757 MB/s
received size  = 512 count = 1953
size, count = 512 1953 wall clock time = 1.897617  8.431088 MB/s
size, count = 512 1953 wall clock time = 1.566108  10.215755 MB/s
received size  = 1024 count = 976
size, count = 1024 976 wall clock time = 3.064925  5.217349 MB/s
size, count = 1024 976 wall clock time = 0.783628  20.406091 MB/s
received size  = 2048 count = 488
size, count = 2048 488 wall clock time = 2.794157  5.722937 MB/s
size, count = 2048 488 wall clock time = 0.407762  39.215974 MB/s
received size  = 4096 count = 244
size, count = 4096 244 wall clock time = 2.972820  5.378995 MB/s
size, count = 4096 244 wall clock time = 0.439282  36.402093 MB/s
received size  = 8192 count = 122
size, count = 8192 122 wall clock time = 3.212721  4.977334 MB/s
size, count = 8192 122 wall clock time = 0.441824  36.192656 MB/s
received size  = 16384 count = 61
size, count = 16384 61 wall clock time = 3.148252  5.079258 MB/s
size, count = 16384 61 wall clock time = 0.475721  33.613786 MB/s
received size  = 32768 count = 30
size, count = 32768 30 wall clock time = 3.008371  5.228291 MB/s
size, count = 32768 30 wall clock time = 0.461200  34.103729 MB/s
received size  = 65536 count = 15
size, count = 65536 15 wall clock time = 2.847520  5.523628 MB/s
size, count = 65536 15 wall clock time = 0.423700  37.122115 MB/s
メッセージサイズが同じので上の数値が MPICH、下が TCP/IPを使ったもの。 なかなかお話にならないほどの速度差があることがわかる。Netgear GA620Tだと

received size  = 1 count = 2000
size, count = 1 2000 wall clock time = 0.588689  0.054358 MB/s
size, count = 1 2000 wall clock time = 0.472337  0.067748 MB/s
received size  = 2 count = 2000
size, count = 2 2000 wall clock time = 0.566266  0.113021 MB/s
size, count = 2 2000 wall clock time = 0.472180  0.135542 MB/s
received size  = 4 count = 2000
size, count = 4 2000 wall clock time = 0.584678  0.218924 MB/s
size, count = 4 2000 wall clock time = 0.469380  0.272700 MB/s
received size  = 8 count = 2000
size, count = 8 2000 wall clock time = 0.591749  0.432616 MB/s
size, count = 8 2000 wall clock time = 0.418707  0.611406 MB/s
received size  = 16 count = 2000
size, count = 16 2000 wall clock time = 0.631375  0.810929 MB/s
size, count = 16 2000 wall clock time = 0.471669  1.085507 MB/s
received size  = 32 count = 2000
size, count = 32 2000 wall clock time = 0.722451  1.417397 MB/s
size, count = 32 2000 wall clock time = 0.503575  2.033461 MB/s
received size  = 64 count = 2000
size, count = 64 2000 wall clock time = 0.538864  3.800588 MB/s
size, count = 64 2000 wall clock time = 0.566205  3.617064 MB/s
received size  = 128 count = 2000
size, count = 128 2000 wall clock time = 0.715044  5.728319 MB/s
size, count = 128 2000 wall clock time = 0.470771  8.700621 MB/s
received size  = 256 count = 2000
size, count = 256 2000 wall clock time = 0.767253  10.677052 MB/s
size, count = 256 2000 wall clock time = 0.707802  11.573858 MB/s
received size  = 512 count = 1953
size, count = 512 1953 wall clock time = 0.953578  16.777837 MB/s
size, count = 512 1953 wall clock time = 0.739233  21.642670 MB/s
received size  = 1024 count = 976
size, count = 1024 976 wall clock time = 0.580449  27.548991 MB/s
size, count = 1024 976 wall clock time = 0.563545  28.375345 MB/s
received size  = 2048 count = 488
size, count = 2048 488 wall clock time = 0.445633  35.883303 MB/s
size, count = 2048 488 wall clock time = 0.364249  43.900694 MB/s
received size  = 4096 count = 244
size, count = 4096 244 wall clock time = 0.403437  39.636384 MB/s
size, count = 4096 244 wall clock time = 0.299399  53.409611 MB/s
received size  = 8192 count = 122
size, count = 8192 122 wall clock time = 0.378661  42.229815 MB/s
size, count = 8192 122 wall clock time = 0.273780  58.407422 MB/s
received size  = 16384 count = 61
size, count = 16384 61 wall clock time = 0.398546  40.122806 MB/s
size, count = 16384 61 wall clock time = 0.238212  67.128373 MB/s
received size  = 32768 count = 30
size, count = 32768 30 wall clock time = 0.377368  41.679846 MB/s
size, count = 32768 30 wall clock time = 0.217963  72.161972 MB/s
received size  = 65536 count = 15
size, count = 65536 15 wall clock time = 0.366703  42.892041 MB/s
size, count = 65536 15 wall clock time = 0.211039  74.529542 MB/s
なお、 fast ether のカードをつけた機械では size=256 辺りでハングした。なんか変なことをしているのかも。

まあ、とにかく GbE のカードでは上ので動くみたいなので、とりあえずこれを使うことにしよう。

2002/4/24 TCP library/GAMMA 再び?

TCP library はいろいろチューニングした。やったことは
  1. なんとか NODELAY というオプションを設定して、短いパケットを受け取ったり送る時に余計な待ち合わせをいれないようにする。これでメッセージが短い時のラウンドトリップ時間がかなり短くなる。
  2. send/receive が並行して動作するようした。これにはソケット動作を非同期にして send/recv がブロックしないようにし、データ転送が終わるまで send/recv を順に見るようにする。これでさらに短いメッセージの時のオーバーヘッドがへらせる。
と、それはいいんだけど、久しぶりに
GAMMA(どんなものかについては牧野が昔書いたメモを参照)をみてみると、いつのまにか Linux Kernel 2.4.16 対応になっている上、Netgear GA621 対応になっているではないか。 GA621 はありがちな NS 83820 を使ったボードで、 83820 はシングルチップで PCI と MIIが実現されていて後は物理層だけで GbE NIC を実現できるしろものなので 83820 を使ったボードであれば (device vendor ID とかの認識を調整すれば)うごきそうなものである。

NS83820 ベースの GbE NIC はこれまでの GbE NIC と違って非インテリジェン トである、つまり、ボード上に CPU をもたない。 DMA チェイニングをする単 純なハードウェアシーケンサがあるだけである。これはもちろんメインの CPU を オフロードして複雑なパケットが飛び交う時のトータルのスループットを上げ るという観点からはマイナスだが、MPI とかで point-to-point の通信さえ速 くなればいいとかオーバーヘッドを小さくしたいとかいう観点からすればむし ろありがたい。実際、 GAMMA のページをみると GA620に比べて 621 ではレイ テンシが 32us から 8.5us (!!!) へと 1/4 近く短縮されている。この 8.5us という数字は、 PCI バスのアクセスレイテンシだけでも 1 us 程度あること を考えれば真に驚異的な数字である。

GAMMA がまともに動くならこっち使うのが正解だよな。かなり驚異というよりは脅威的な速度がでるし、、、(というわけでまたはまるのである)

それはいいんだが負荷を思い切りかけて計算すると g6host12 が落ちる、、、これは GA7DXR に 1.5GB メモリを積んだ怖い機械なので、とりあえず K7S6A 1GB に交換。

2002/4/25 TCP library/GAMMA 再びだめ

さて、 GAMMA である。

Planex のカードをいきなり GA621 であると認識したので、テスト用の2台に のせてみる。手順は上と全然変わらない。動かしてみると例によって動かない。 USE_JUMBO_FRAME を消すと動く。で、速度は大変素晴らしい。レイテンシ 30us、スループット 80MB/s くらいでる。

ということで、0号クラスタに入れることにする。まず2台入れる。 GAMMA は 基本的には専用の NIC を必要とするので、100BT のカードを別に付ける。こ れはその辺に転がっいた Tulip 互換を付ける。で、2枚で動かしてみると特に 文句はいわないで動く。で、4台にしてみる。これも OK。

ここで全部にいれた。すると、、、、動かない。というか、全部に設定しても 一度に4台以上でジョブを起動すると死んでしまう。これはちょっと原因が分 からない。考えられる一つは、スイッチをカスケードしていると動かないので はないかということ。割合豪快な作りみたいなのでまあそういうこともあるか も。

ということで、あんまり考えてもしょうがないので元に戻す。といっても 100BT のカードは折角つけたのでそのままにしておく。

まあ、 TCP/IP でもレイテンシは長いとはいえスループットでは最高で 60MB/s くらいでるので、満足するべきとはいえる。 32bit/33MHz の PCI バ スでこれだけでれば立派なものである。なお、 RTL8139 のカードは依然とし てたたっていて、2.4.17 についてくるこれ用のドライバは余計なことに NS83820 も認識したがる。この動きもなんか不思議で、 RTL8139 なチップが のった NIC と NS83820 がのった NIC が両方あるとついでに 83820 も認識 してしまうみたい。認識されてしまうと gigabit にならないので(なってたか もしれない。そういえばチェックしてないや)、ドライバをはずしてカーネル再構築した。

2002/8/16 メモ

こんなのがあったのでリンク。 保存

まあ、とりあえず半年くらいほぼ安定運用できてるから、ケースなしでも十分使えるということですわね。