考察: なぜ HTTP (TCP) の並列ダウンロードが有効か
Published: 2023/10/28
巨大な単一ファイル(e.g. .iso
)をダウンロードするとき、 wget
や curl
を利用するよりも、 aria2c
や axel
を用いてダウンロードすると早くなる、という記事をみかけたりする。
wgetより速いaria2の使い方メモ
自分はwgetの代わりにaria2を使っているのですが、たまに引数等を忘れるのでよく使うものについてメモを残し…
hrvtuirvw.net

これらの並列ダウンロードのツールは、内容的には HTTP の範囲リクエスト を利用して、対象ファイルをセグメントに分割し、そのセグメントそれぞれを別々の HTTP コネクションでダウンロードする仕組みである、と理解できる。
同じサーバーに対する接続を複数化するだけでスピードアップするのは、よくよく考えれば不思議である気がしたので、調べた内容をここにまとめる。
TCP のウィンドウ制御
TCP は、送信したバイト列が受信側で漏れなく同じ順番で読み出せるようにするために、送信したい内容を IP パケットに分割し、そのパケットを元々の順番で送っていく。
受け取る側は、パケットが順不同で到着することになるため、ある程度のバッファを用意しておいて、そこに到着したパケットの内容をつめておいて、シーケンス番号上で漏れがないことを確かめられたところから順に、 ACK
パケットで、どこまでのシーケンス番号を受信できたかを送信側に返信する。
そしてその内容を上位層のアプリケーションに引き渡していく。
TCP/UDP - TCP ヘッダ
TCP/UDP - TCP ヘッダ
www.itbook.info

すべての TCP パケットには、ウィンドウサイズを表すフィールドがあり、これは、(ACK)パケットを送っている側が、今どれだけの受信用に用意しているバッファがあるか、を表す。 なので、理屈上は、このウィンドウサイズにおさまるぐらいのデータを、送信側は一気に送ってしまっても良いが、ネットワークは帯域幅というものがあり、あまりに一気に送るとパケットロスが発生し、データが相手側に到達しなくなるリスクが高くなるので、少しずつ一度に送るパケット数を増やす、という制御を行う。 これは輻輳制御と呼ばれる。
輻輳制御では、上記(ACK)パケットから読み取れた最新の受信ウィンドウ(rwnd)とは別に、輻輳制御のためのウィンドウというものをデータ送信側は計算する。 いくつかのプロトコルがあるが、基本的にはなるべく多くの、しかしパケットロスが起きないぐらいのウィンドウサイズになるように自動的に調整されるようなアルゴリズムで、この輻輳制御ウィンドウ(cwnd)は決まっていく。
rwnd と cwnd の小さい方までを、データ送信側はとりあえずパケットとして投げていき、暫くたっても ACK
が来なければ来ていない部分から再送していく。
https://www.infraexpert.com/study/tcpip10.html
https://www.infraexpert.com/study/tcpip11.html
TCP の理論値: rwnd が常に BDP より大きければ、帯域幅をフル活用できる(かも)
Bandwidth-Delay Product (BDP) は、帯域幅にラウンドトリップの時間をかけ算したもの。 ネットワークの状態によって存在しているはずの、理論的に到達したい輻輳ウィンドウ幅を表す値となる。 rwind が常にこれよりも大きければ、輻輳制御により、最終的に BDP まで cwnd は拡大していく(と嬉しい)。
仮説: 複数コネクションが効く理由
特に、送信側も受信側も、 BDP の数倍分の、ソケットバッファ(カーネルが通信のために持っておくためのバッファ) を保持していて、かつ、諸々の TCP 高速化の手法(特に例えば、ウィンドウスケーリング) を on にしている場合には、帯域幅のぎりぎりまでの速度を出すのは可能にはなりそうではある。
ただし一般的には、クライアント側がサーバー側の設定をいじることはできないので、常に自分にとって最適なサーバーの TCP 設定がなされていることは期待できない。 そのような場合においては、 TCP 接続を増やすことでチューニングされきらない TCP 接続1本あたりのスピードを量でカバーできて、スピードアップがなされている、という仮説が思い付く。 一般的に HTTP での配布は、全世界の平均的なユーザー接続に対してチューニングするものであり、全世界のユーザー(の平均)はそこまで帯域幅は大きくないであろうから、コネクションの量を用意することで、そこまで大きくない帯域にチューニングされたサーバー側の TCP 接続設定を上手く利用できて、ダウンロードが速くなる、という仮説である。
もしくは、ボトルネックがネットワーク部分ではなく、例えばサーバー側のアプリケーションレイヤーにあるような場合に(e.g. ディスクから読み出していてネットワークの帯域に追い付いていない)、 DoS じみたことにはなるが、リクエスト数を増やすことで、遅いサーバーから N 人で手分けしてダウンロードしていた、といったような効果が期待できる場合も、ありえそうではある。
メモ: on Linux
Linux の場合には、次の資料などが参考になりそう。 具体的に cwnd 等を確認する方法等について述べられている。
LinuxサーバーのTCPネットワークのパフォーマンスを決定するカーネルパラメータ – 1編 | NHN Cloud Meetup
1.はじめに ネットワークのパフォーマンスを決定付ける最も重要な要素は、最終的にはアプリケーションにあるでしょう。ただ、ワークロードの特性によっては、デフォルト設定されたTCPカーネルパラメータが制約となり、パフォーマンスを発揮できないときもありますね。 非常にたくさんのカーネルパラメータがありますが、本文では、ネットワーク帯域幅(bandwidth)のカーネルパラメータ、ネットワーク容量(capacity)のカーネルパラメータを主に扱います。 2.準備 Linuxはsysctlコマンドで簡単にカーネルパラメータを実行時に変更できます。 次のようなコマンドを使用すると、現在のカーネルパラメータ設定値の全体を閲覧できます。 $ sysctl -a ここで取り上げるネットワークは、特にTCPのcapacityとbandwidthなどを調整(tuning)できるカーネルパラメータのごく一部を紹介します。 通常、TCPと関連カーネルパラメータは、net.core、net.ipv4、net.ipv6などの接頭辞を付けています。 また、次のようなコマンドで、現在の設定値を変更できます。 例えば、net.core.wmem_maxという設定値を16777216に変更するには、次のように入力します。 $ sysctl -w net.core.wmem_max="16777216" システム起動時に設定されるようにするには、/etc/sysctl.confファイルに対応する設定値を記入します。 3. TCP帯域幅(bandwidth)関連パラメータ 3.1 BDP TCPの帯域幅を理解するには、まずBDP(Bandwidth Delay Product)について理解する必要があります。 100Mbpsの帯域幅を持つネットワークで、2つのhost A、B間のRTT(Round-trip time)が2秒であるネットワークパスがあると仮定します。 AからBまでデータが連続送信されているとき、Aから出発して、まだBに到着していないデータ量(bits in flight)はどれ位あるでしょうか? 帯域幅を幅で、遅延時間RTTを長さで考えると、2つの積がネットワークパス上のフローティングデータ量の最大値を表します。
meetup-jp.toast.com
