コンピューターサイエンス:アルゴリズム / OS / ネットワーク / DB
輻輳制御と再送
全員が全力で送ると、全員が損をする
回線には運べる量の上限があります。上限を超えた分は中継機が捨てます。捨てられた分は送り直されます。送り直しでさらに量が増え、もっと捨てられる。この悪循環に入ると、回線は埋まっているのに誰のデータも進まない状態になります。
インターネットは無数の通信が同じ道を共有しています。全員が自分の都合だけで全力を出せば、全員が損をする。この結末を避けるために、TCP は誰に言われるでもなく、自分から送る量を絞ります。
混雑は、届かなかったことでしか分からない
厄介なのは、混雑を知らせてくれる人がいないことです。中継機は「いま混んでいます」と教えてくれません。黙って捨てるだけです。
そこで TCP は逆から推測します。有線の回線でデータが勝手に壊れて消えることは、めったにありません。だから「返事が来ない」のは、混雑した中継機に捨てられたサインだ、とみなします。消えた気配を感じたら送る量を減らし、しばらく何も起きなければ少しずつ増やす。この上げ下げを延々と繰り返しています。
無線ではこの前提が崩れます。電波の乱れで消えただけなのに、TCP は混雑だと誤解して速度を落とします。Wi-Fi が不安定な場所で、回線が空いているのに遅い、という現象が起きるのはこのためです。
探りながら上げて、ぶつかったら下げる
送ってよい量は、返事を待たずに送り出せるバイト数として管理されます。動きは次のようになります。
- 最初はごく小さい量から始める。返事が返ってくるたびに増やすので、1 往復ごとにおよそ倍のペースで増えていく
- ある程度まで来たら、増やし方を倍から少しずつに切り替える。上限が近いので、ここから先は慎重に探る
- 消えた気配を検知したら、量を大きく減らして 2 の探り方に戻る
最初が慎重なのは、上限を知らないまま全力を出すと確実にぶつかるからです。それでいて倍々で増やすのは、慎重すぎると回線が空いているのに使いきれないからです。「知らない道をだんだん加速しながら走り、壁にぶつかったら下がってまた寄っていく」という動きだと思ってください。
この上げ下げを、同じ回線を使う全員が同時にやっています。誰かが増やせば誰かが押し出され、押し出された側が減らすとまた空きができる。中央で誰かが割り当てているわけではないのに、結果として全員がおおむね公平な取り分に落ち着きます。取り決めを守る実装しかいない、という前提の上に成り立っている均衡です。
時間切れを待たずに気づく方法がある
消えたことを知る一番素朴な方法は、返事が一定時間来ないのを待つことです。ただしこれは遅く、しかも量を大きく落としてしまいます。
そこで、もっと早く気づく手段が使われます。1 つだけ抜けた場合、受け取り側は後続が届くたびに「まだそこまでしか受け取れていません」と同じ返事を繰り返します。送り主はこの繰り返しを 3 回受け取った時点で、時間切れを待たずに送り直します。復帰が速く、量を落とす幅も小さくて済みます。1 つ落ちただけで速度が半分になる、という事態を避けるための工夫です。
この慎重な立ち上がりは、体感としても現れます。大きなファイルの転送が最初だけ遅く、途中から伸びるのはこのためです。逆に短い通信は、本気を出す前に終わってしまいます。小さなファイルを大量に送るより、まとめて 1 本で送るほうが速いことがあるのは、この立ち上がりの分を 1 回で済ませられるからです。
復習ミニクイズ
TCP の輻輳制御で、最初に送信ウィンドウが指数関数的に増えるフェーズはどれですか