TCP通信プログラム実装時の体験談。
次のようなよくあるTCP送受信を行うプログラムを作りました。
送受信共にヘッダは20バイト未満、データと合わせても1KB未満のサイズで、サーバ側の処理も一瞬で終わるようなものです。
ところが、このプログラムの送受信1回あたりの所要時間を計測したところ、約300ミリ秒も掛かっていました。
3〜4回送受信するだけで1秒経ってしまう計算です。
何がボトルネックになっているのか細かく計測したところ、所要時間の9割以上は select
関数での受信待ち時間でした*1。
ちなみに、 send
関数や recv
関数は一瞬で終わっていました。
そこで、次のようにヘッダとデータを一度に送信することで送信回数を減らしてみました。
要するに、今まで send(ヘッダ); send(データ);
とやっていたところを send(ヘッダ+データ);
にしてみたということです。
受信側の処理は変えていません。
そして再び送受信1回あたりに掛かる時間を計測してみました。
予想では半分の150ミリ秒くらいにはなるかと思っていましたが、結果はなんと約15〜30ミリ秒。
分けて送っていた時の10倍以上もの速度になりました。
「TCP通信ではNバイトのデータを1回で送信しても1バイトずつN回に分けて送信しても所要時間は同じ」というような話をどこかで聞いたような気がするのですが、一体どうしてここまで差が出たのでしょうか…。
まだまだソケットプログラミングは勉強不足だなと感じた一件でした。
前者の方法だと、送信側のヘッダ情報送信とデータ送信の間に受信側のヘッダ情報受信が挟まってしまって通信回数が増えているのではと予想。
つまり実際には次のような流れになってしまっているのではなかろうか。
上述した「TCP通信ではNバイトのデータを1回で送信しても1バイトずつN回に分けて送信しても所要時間は同じ」
というのは、送信側が送信…というか正確には send
関数の呼び出しをすべて終えた後で受信側が recv
関数を呼んだ場合の話なのだろう。
送信側が send
関数を呼んだ時点ではまだデータは送信側の内部バッファに追加されるだけで、受信側が recv
関数を呼んだ時点で初めて送信処理が行われるので、それならば確かに send
関数を何度呼ぼうが所要時間はほぼ同じになる(内部バッファへのデータ追加に掛かる時間なんて無視できるレベルなので)。
とはいえ、これが速度に10倍もの差が付いた理由になるかは疑問だが…。