Ilya Grigorikの "High Performance Browser Networking "にTCP最適化についての詳細な説明があり、読むととても勉強になるので、理解しやすいように要約してみました。理解しやすいように要約しました。
フロー制御
データを送信するとき、送信側が受信側の処理能力を超えるデータを送信すると、受信側はパケットを失うことになります。このような問題を避けるために、フロー制御では、データ送信の双方がそれぞれの受信ウィンドウの大きさ「rwnd」を相互作用のたびに宣言する必要があります。これは、最大でどれだけのデータを保存できるかを示すために使われます。これは主に受信側のためのもので、平たく言えば、送信側が受信側の食べられる量を知っているということです。ウィンドウがゼロに減れば、満杯で消化しなければならないことを意味し、持ちこたえるのが難しい場合は失禁する可能性があります。もしウィンドウがゼロに減衰するなら、それは受信者が満杯で、消化しなければならないことを意味し、もし懸命に持ちこたえるなら、失禁するかもしれず、それはパケットロスです。
フロー制御
ユーザーの立場に立てば、受信者と送信者という名称は相対的なものです。ウェブを閲覧する場合、データは主に次の行にあり、この時クライアントは受信者、サーバーは送信者です。ファイルをアップロードする場合、データは主に次の行にあり、この時クライアントは送信者、サーバーは受信者です。
出遅れ
フロー制御は送信側による受信側の過負荷を避けることはできますが、受信ウィンドウ「rwnd」は個々のサーバーの状況を反映するだけで、ネットワーク全体の状況を反映しないため、ネットワークの過負荷を避けることはできません。
ネットワークが過負荷になる問題を回避するために、スロースタートでは輻輳ウィンドウ "cwnd "という概念を導入しています。"cwnd "は、送信者が受信者からの確認応答を受信する前に送信できる未確認データの最大量を示すために使用されます。cwnd "と "rwnd "の違いは、"cwnd "は送信側の内部パラメータに過ぎず、受信側に通知する必要がないこと、初期値が比較的小さいことが多いこと、そして、パケットが受信側に確認されるにつれて、ウィンドウが指数関数的に拡大していくことです。これは、ボクシングの試合に少し似ていて、最初は敵のことを知らないので、サブパンチを試しがちで、敵の状況をよく理解できるようになってから、徐々に激しい攻撃の強度を上げていくことに似ています。
スロースタート
低速起動時には、"cwnd "が増加するにつれて、ネットワークの過負荷が発生する可能性があります。
混雑回避
説明:ネットワークで実際に送信される未承認データのサイズは、"rwnd "と "cwnd "の小さい方に依存します。
輻輳回避
スロースタートの導入から、送信者は "cwnd "の大きさを制御することで、ネットワークの過負荷を避けることができることがわかります。このプロセスでは、パケットロスはネットワークの問題というよりも、一種のフィードバックメカニズムであり、それを通じてネットワークの輻輳が発生したことを感知し、データ伝送戦略を調整することができます。実際には、スロースタートのしきい値 "ssthresh "の概念があり、もし "cwnd "が "ssthresh "よりも小さい場合、それはスロースタートフェーズにあることを意味し、もし "cwnd "が "ssthresh "よりも大きい場合、それは輻輳回避フェーズにあることを意味し、この時、"cwnd "はもはやスロースタートフェーズのように指数関数的に丸められないが、直線的に成長する傾向があり、ネットワークの輻輳を回避するために、このフェーズの実装のための様々なアルゴリズムがあり、通常はデフォルトを保つことができ、ここでは説明しません。このフェーズを実装するためのいくつかのアルゴリズムがあり、それは通常、デフォルトのままで十分ですので、私はここでそれらすべてを説明しません。
...
rwnd」を妥当な値に調整する方法
例えば、明らかに100メガビットのネットワークでは、最大伝送データの理論値は10メガバイトになるはずですが、実際の状況はそれとは程遠く、1メガバイトにしかならないことがあります。このような問題を除外すると、受信ウィンドウの "rwnd "の不合理な設定に起因することがほとんどです。
実際、受信ウィンドウ「rwnd」の妥当な値は、帯域幅と待ち時間の積であるBDPのサイズに依存します。帯域幅が100Mbpsで待ち時間が100msだとすると、計算は以下のようになります:
BDP = 100Mbps * 100ms = (100 / 8) * (100 / 1000) = 1.25MB
Linuxでは、カーネル・パラメータで受信バッファのサイズを設定することで、受信ウィンドウのサイズを制御できます:
shell> sysctl -a | grep mem
net.ipv4.tcp_rmem = <MIN> <DEFAULT> <MAX>
転送性能のために十分な大きさのバッファを設定した場合、大量のリクエストが同時に来たときにメモリが爆発するでしょうか?Linuxにはバッファサイズの自動調整メカニズムがあり、ウィンドウの実際のサイズは、性能とリソースのバランスを見つけるために、自動的に最小値と最大値の間を浮動します。
バッファサイズ自動調整機構の状態は、以下の方法で確認できます:
shell> sysctl -a | grep tcp_moderate_rcvbuf
バッファサイズ自動調整メカニズムがオフの場合は、バッファのデフォルト値をBDPに設定し、バッファサイズ自動調整メカニズムがオンの場合は、バッファの最大値をBDPに設定します。
言い換えれば、すべての領域がデータの保存に使われるわけではなく、対応する余分なオーバーヘッドは以下のように計算されます:
Buffer / 2^tcp_adv_win_scale
Linux カーネルのバージョンによって、net.ipv4.tcp_adv_win_scale の値は 1 または 2 になることがあります。このロジックに従い、バッファの最終的な妥当値は以下のように計算されます:
BDP / (1 – 1 / 2^tcp_adv_win_scale)
BDPのレイテンシとはRTTのことで、通常はpingコマンドで簡単に取得できますが、ICMPがブロックされている場合はpingは役に立ちません。
cwnd」を妥当な値に調整する方法
一般的に「cwnd」の初期値はMSSのサイズに依存し、以下のように計算されます:
min(4 * MSS, max(2 * MSS, 4380))
イーサネットの標準的なMSSサイズは通常1460なので、'cwnd'の初期値は3MSSです。
しかし、ウェブブラウジングの場合は状況が異なります。これは、転送されるデータ量が比較的少なく、時間も比較的短いためです。これに対して、スロースタート段階での「cwnd」の初期値が比較的小さいと、フルウィンドウまで加速する前に通信が終了してしまう可能性が非常に高くなります。これは、ボルトが100mレースに参加するようなもので、スタートが遅いと、たとえ加速が速くても、走りきる前にゴールが見えてしまい、良い結果が得られない可能性があります。
例:ウェブページが20KB、MSSサイズが1460Bで、ウェブページ全体が15MSSだとします。
cwnd "の初期値が小さいときに何が起こるか見てみましょう:
小窓
cwnd "の初期値が大きいときに何が起こるかをもう一度見てください:
大きな窓
明らかに、TCPハンドシェイクとサーバー側の処理を除けば、本来3RTTを要するデータ転送が、「cwnd」の初期値を増やした後は1RTTで済むようになり、大幅な効率化が図られています。
推奨:Dana mnotが相関効果のテストに使えるhtracrというツールを書きました。
cwnd」の初期値を大きくするのはとても良いことなので、どの程度の大きさに設定すれば良いのでしょうか? Googleはこの分野で多くの研究を行い、効率と安定性を天秤にかけた結果、最終的に10MSSという提案をしました。 Linuxのバージョンがそれほど古くないのであれば、以下の方法で「cwnd」の初期値を調整することができます:
shell> ip route | while read p; do
ip route change $p initcwnd 10;
done
これは、前述したように、ネットワーク上で実際に送信される未承認データのサイズは「rwnd」と「cwnd」の小さい方に依存するため、受信側の「rwnd」が小さくなると「cwnd」が機能しなくなるからです。
ターゲットサーバーの "cwnd "の初期値をチェックして、パケットをカウントしたい場合もあるでしょう:
テスト開始
ハンドシェイクフェーズでRTTが168であることが確認され、送信を開始してから最初のパケットを取得するまでの時間は409で、RTTを加算すると577となり、409と577の間に2つのパケットがあるので、"cwnd "の初期値は2MSSとなります。
追記:ある人がinitcwnd_checkというスクリプトを書いていて、"cwnd "の初期値をチェックするのに役立っています。
...




