TCP Slow Start と MSS, initcwnd などのメモ

現実的な値と根拠など

Slow Start などの例に良く出てくる数字の論拠と、個々の単位がどこで決まってくるかを主に取り扱う。というメモ。苦手科目だ。_:(´ཀ`」 ∠):

Max Segment Size (MSS) の決定

この MSS がウインドウサイズ(一度に転送するデータサイズ)の単位として扱われる。(後述)

TCP コネクションが 3-way handshake によって確立される際、ノード間で下記のような MSS に関するやり取りが発生する。

  1. 各ノードは Maximum Transmission Unit (MTU) から TCPヘッダ20バイト、IPヘッダ20バイトの計40バイトを引いた値を MSS として提示する
  2. 各ノードの希望 MSS のうち低い方を、そのコネクションにおける MSS とする

たとえばイーサネットでは最大1,500バイト(オクテット)がIP通信に利用できる。PPPoEを使うとカプセル化のために8バイトを使うため、1,492バイトとなる。 Maximum Transmission Unit - Wikipedia

このようにMTUはおおよそ 1,500 と扱われている。MSS がおおよそ1,460 として扱われて登場するのは 1,500 - 40 = 1,460 という計算によるものだ。

初期ウインドウサイズの決定

スロースタート開始時の Congestion Window Size (CWND) を特に、初期ウインドウサイズ として決定する。ここで決定された CWND からスロースタートが開始する。

ウィンドウ・サイズがMSSの整数倍になっていれば、最も大きなセグメント・サイズばかりでウィンドウをいっぱいにすることができる。だが整数倍でなければ、最後のセグメントはMSSのサイズよりも小さなパケットにしなければならない。これではネットワークの効率を最大限に生かすことができない 基礎から学ぶWindowsネットワーク:第16回 信頼性のある通信を実現するTCPプロトコル(3) (2/4) - @IT

CWND は、このような事情から MSS を等数倍にするのが妥当であり、Linux における CWND に関するパラメータ initcwnd も MSS を単位として、そのセグメントが n 個分というものになっている。

Originally, the cwnd start value was set to 1 network segment; RFC 2581 updated this value to a maximum of 4 segments in April 1999, and most recently the value was increased once more to 10 segments by RFC 6928 in April 2013. High Performance Browser Networking

IW = min (4*SMSS, max (2*SMSS, 4380 bytes)) RFC 2581 - TCP Congestion Control

ウインドウサイズの初期値 initcwnd は、Linux Kernel 2.6.32 までは 4 セグメント とされていた (RFC 2581いわく 3 or 4 が初期ウインドウになる) 。2.6.33 以降は RFC 6928 に倣って 10 セグメントに設定されている。

少なくとも 3 * MSS くらいになるので、1,460 * 3 = 4,380 およそ 4KB として 初期ウインドウサイズ が紹介されていることが多い。

initcwndの変更

現行のサーバーの多くは Linux Kernel 2.6.32 以下で稼働しているだろうが、initcwnd を変更すること自体はあまり難しくない。

% ip route show
**.***.***.0/24 dev eth0  proto kernel  scope link  src **.***.***.**
169.254.0.0/16 dev eth0  scope link
default via **.***.***.1 dev eth0

% sudo ip route change default via **.***.***.1 dev eth0 proto static initcwnd 10

% ip route show
**.***.***.0/23 dev eth0  proto kernel  scope link  src **.***.***.**
169.254.0.0/16 dev eth0  scope link
default via **.***.***.1 dev eth0  proto static  initcwnd 10

コマンドをちょっと叩けば変更できるようになっている。Linux Kernel 2.6.32 以下のところで initcwnd を 10 に設定してみた。

そもそもSlow Start

Slow Start そのものについても、おさらいがてら軽く復習しておく。

基本戦略

全体感として、@IT:TCP/IPアレルギー撲滅ドリル【下位レイヤ編】4-2 とか 3 Minutes Networking No.42 に、わりと分かりやすい図が示されていた。

データを一度に送信しすぎると、ネットワークの転送許容量を超えてしまって輻輳してしまう可能性がある。そのために、小さいウインドウサイズから始めて、倍々でサイズを増やして転送速度を上げていき、輻輳が発生するときにはウインドウサイズを落として輻輳を制御する。

輻輳 (ふくそう) → ネットワークの許容量がパンクして、転送データの損失などが起こりうる状態。

ウインドウサイズの上限は、接続の確立時に Receive Window Size (RWIN または RWND) として交換している。スロースタートは、この上限に向けて輻輳を避けつつ、送信量をコントロールしながら速度を伸ばしていくための仕組みである。

最初から上限サイズで送らないのは、ネットワークである以上、中継地点における輻輳が起こらないことが保証できないからである。上限サイズはあくまで2者間の話であるため、不特定な中継地点を通して、確実にデータを転送するために必要となる。

initcwndの拡張は何が嬉しいの?

Or put slightly differently, a 10Mbps connection, on average uses only 16% of its capacity. Yikes! As it turns out, if we want faster internet, we should focus on cutting down the round-trip time between the client and server, not necessarily just investing in bigger pipes. Faster Web vs. TCP Slow-Start - igvita.com

ここで述べられているのは、2011年当時の記事だが 10Mbps のコネクションのうち平均して 16% しか使われていないということである。このことから、Webを高速化する上で注力すべきは、帯域を太くすることではなく、スロースタートのようなラウンドトリップを削ることであるとしている。

確かにたとえば initcwnd が 10 だと仮定して 1,460 * 10 = 14,600 が初期値だとすれば、およそ 14KB くらいまでのファイルであれば、スロースタートによるラウンドトリップを1回分節約することが可能になる。

参考

多くの情報を参考にさせていただいている。これらの情報を元に、この記事を組み合げたのだが、ちゃんとしているかはあまり自信がない :;(∩´﹏`∩);: