データ転送速度が劇的に改善されるマジック

改造Firefoxで日米間6.5GbpsのWebアクセス。東大が世界最速達成 -BB Watch より。私は 1Gbps 以上の世界は完全に未知の世界なので、今回の記事は「どうやって 6.5Gbps ものデータ転送速度を達成するか」ではなく「何故、改善前は(そんな高速ネットワーク環境下で) 6Mbps 程度のデータ転送速度しか出せないのか」と言うお話です。

TCP のデータ転送(ダウンロード)では、ソケットバッファの値を弄っただけで劇的にデータ転送速度が改善される場合があります。

ソケットバッファとは?

あるサーバ/クライアント間でデータ通信をする際、パケットが、受信ホストに到着するタイミングと(受信ホストの)アプリケーションが、到着したパケットを読み込むタイミングは非同期です。そのため、パケット(データ)は到着したのだけれどアプリケーションがまだ読み込みに来てくれないと言う場合に、そのデータを一時保存する必要があります。このためのバッファがソケットバッファと呼ばれます。

TCP のデータ転送速度の議論をする際にしばしば言われる仮定として「送受信ホストのバッファサイズは十分に大きいと仮定する」と言うものがあります。しかし、実機でデータ転送実験をする場合、受信ホスト側のソケットバッファ不足がボトルネックとなっている(これのせいでデータ転送速度が低い)事例が多々存在します。

ソケットバッファはどれ位必要なのか?

一般的に、ソケットバッファは帯域遅延積(ネットワーク帯域 * RTT)分は必要、と言われています。例えば、改造Firefoxで日米間6.5GbpsのWebアクセス。東大が世界最速達成 -BB Watch の実験環境では 10Gbps のネットワークで RTT(1 パケットが往復するのに要する時間)が 136ms だそうなので、

10Gbps * 0.136sec = 1.36Gbit = 170MByte

のバッファが必要となります。なぜこれだけのソケットバッファが必要かと言うと、それは TCP がデータ転送速度を調節するための仕組みに起因します。

  • TCP は「1 RTT にどれだけ(何パケット)送信できるか」と言う形でデータ転送速度を調節する。
  • TCP は送れると判断した量のパケットを一気に送信してしまう(TCP のバースト性)。

TCP は、1 RTT 単位でその時に送れる量のパケットを一気に送信してしまうため、受信ホストではそれを受け止めるだけのバッファを必要とします*1。尚、実際には、受信側の TCP は一度に受け止められる量を送信側の TCP へ通知し続けており、送信側 TCP はたとえネットワークが空いていたとしても、これを超える量のパケットは送信しないようになっています(これも、ソケットバッファが小さいとデータ転送速度が低下する原因)。

現在のソケットバッファは意外と小さい

現在のカーネル (Windows, Linux, ...) で用意されているソケットバッファは意外と小さいと言う問題があります。ソケットバッファのバッファサイズがいくらかを知るにはいくつか方法がありますが、簡単な方法として Iperf を利用すると言うものがあります(Iperf は、ある 2 ホスト間でのデータ転送速度を計測するツールですが、その際にソケットバッファサイズを表示してくれる)。

上記の画像は Windows Vista (+cygwin) で Iperf を実行した結果ですが、見ると分かるように ソケットバッファが 64KByte しかありません。Linux 環境は手元にないので確証はないのですが、linux kernel 2.6.x だとソケットバッファのデフォルト値は 110KByte だそうです(参考: Linux におけるソケット機能の向上)。

ソケットバッファが 110KByte だとして、上記の記事の RTT が 136ms の場合にどれくらいのデータ転送速度が出せるのかを計算してみた結果が下記となります。

110KByte * 8 / 0.136sec ≒ 6.47Mbps

この結果を見ると、改善前のデータ転送速度が 6Mbps 程度で頭打ちになってしまった原因の一端が何となく見えてきます。

この問題は、海外と通信を行う際(RTT が大きい場合)に問題となりやすいと言う性質があります。なぜならば、日本国内で通信を行う限りでは RTT は高々 20ms 程度なので、110KByte のソケットバッファでも 50Mbps 程度のデータ転送速度は達成できるためです。

RWIN 調整問題の実体

一時期(2000年前後か。ADSL 普及前後)、「RWIN を最適化してデータ転送速度を改善しよう」と言う話題が人気を集めていましたが、この RWIN 調整問題の実体が今回述べたソケットバッファの話となります*2

私が試した(見てたを含む)限りだと、500Mbps 位までは高速なネットワーク環境さえあれば大陸間通信 (RTT が 100ms を超えるようなネットワーク環境)であろうとも、ソケットバッファのバッファサイズを調整するだけでデータ転送速度をかなりのところまで改善する事ができるようです(その分,メモリを消費しますが)。光通信も普及してネットワーク帯域はどんどんと広くなっていますので、ぼちぼち RWIN の調整みたいな話題が再び人気になるかもしれません。RWIN の調整方法に関する詳細は ブロードバンドスピードテスト(回線速度・通信速度測定診断サイト) - 速度向上のポイント 辺りを参照して下さい。

蛇足

手元にあるマシンで Iperf を使って localhost (loopback interface) への速度を計測したところ 3.35Gbps と言う結果が出ました。この結果を見ると、十分なソケットバッファと後は NIC (Network Interface Card) が頑張ってくれれば(10Gbps NIC とか)、TCP の他の部分は特に触らずともいい線まで行くのかもしれません。

[clown@stinger ~]$ iperf -c localhost -t 60 -i 1
------------------------------------------------------------
Client connecting to localhost, TCP port 5001
TCP window size: 64.0 KByte (default)
------------------------------------------------------------
[  3] local 127.0.0.1 port 57734 connected with 127.0.0.1 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0- 1.0 sec    350 MBytes  2.94 Gbits/sec
[ ID] Interval       Transfer     Bandwidth
[  3]  1.0- 2.0 sec    381 MBytes  3.20 Gbits/sec
[ ID] Interval       Transfer     Bandwidth
[  3]  2.0- 3.0 sec    365 MBytes  3.06 Gbits/sec
[ ID] Interval       Transfer     Bandwidth
[  3]  3.0- 4.0 sec    416 MBytes  3.49 Gbits/sec
[ ID] Interval       Transfer     Bandwidth
[  3]  4.0- 5.0 sec    383 MBytes  3.21 Gbits/sec
・・・(中略)・・・
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-60.0 sec  23.4 GBytes  3.35 Gbits/sec

もちろん、http なんかで同じ速度を出そうとすると、記事に触れられていたように Apache などのアプリケーションがボトルネックになったりするので、そう単純にはいかないでしょうが。

*1:この TCP のバースト性を改善するための仕組みもある程度組み込まれてはいますが。

*2:もっとも、あの当時は 1 パケットのパケットサイズが小さかったらしく、そっち (MTU) を調整してヘッダのオーバーヘッドを減らすと言う話の方が人気でしたが。