CoDel と FQ-CoDel:遅延を直接狙うAQMの原理
ダウンロード中でも通話やSSHが固まらない、その裏側が腑に落ちる。CoDelがキュー長でなく滞留時間で輻輳を見抜く原理と、FQ-CoDelのフロー分離、target/intervalの正しい意味を内部から解説。
- 1.CoDel はキュー長ではなくパケットの滞留時間(sojourn time)を測り、target が interval の間継続して超え続けたときだけドロップに入る。
- 2.target はバースト用の一時キューを許す下限、interval は最悪 RTT 相当の観測窓で、両者は時間の量なのでリンク速度に依存しない。
- 3.FQ-CoDel は 5-タプルのハッシュでフローを別キューに分け、新規・低レートのフローを先回しすることで、エレファントフローの裏でも対話的応答性を保つ。
CoDel が解こうとした問題
CoDel(Controlled Delay, RFC 8289)は Kathleen Nichols と Van Jacobson が設計した AQM(Active Queue Management)です。出発点は、過大なバッファに定常的なキューが居座って遅延が肥大するバッファブロートを、現場ごとの再調整なしで潰すことでした。
従来の RED(Random Early Detection)は平均キュー長をしきい値と比べて確率的に落とす方式ですが、適切なしきい値がリンク速度・RTT・フロー数に依存し、固定値で当てるのが事実上不可能でした。CoDel の核心はここを発想転換した点にあります。問題はキューが長いことではなく、キューが長く居座ることです。バーストを吸収する一時的なキュー(good queue)は有益で落としてはならず、いつまでも掃けない定常キュー(bad queue)だけを崩したい。この両者を見分ける指標が、キュー長ではなくパケットの**滞留時間(sojourn time)**だ、というのが CoDel の土台です。
滞留時間とは、あるパケットがキューに入ってから取り出されるまでの実時間です。CoDel はエンキュー時刻をパケットに刻み、デキュー(取り出し)の瞬間に now - enqueue_time で実測します。キュー長(バイト数)と違い、これはボトルネックの実際の送出レートを織り込んだ「本物の遅延」です。だからリンク速度がいくらでも、同じ閾値判定がそのまま通用します。
target と interval の正確な意味
CoDel のパラメータは実質 2 つだけで、どちらも時間で表されます。ここを取り違えると挙動が読めません。
target(目標遅延, 既定 5ms): 許容するキュー遅延の下限。滞留時間がこれ未満なら一時バーストとみなし、絶対に落としません。設計上の目安はintervalの 5〜10%(既定なら 100ms に対して 5ms)で、小さくしすぎるとバースト吸収力を失ってスループットが落ちます。interval(観測窓, 既定 100ms): 滞留時間がtargetを超え続けてよい猶予時間。おおむね典型的な最悪 RTT に相当します。1 回の RTT 内では、送信側が減速シグナルに反応する時間を与えるため、超過しても即座には落としません。
重要なのは、CoDel が見るのは「target を interval の間、継続して超えたか」という一点だということです。瞬間的に target を超えても、interval 内にキューが捌けて滞留時間が target を下回れば、それは健全なバーストであり何もしません。interval の全期間にわたって滞留時間が target 以上であり続けたときに初めて「これは掃けない定常キューだ」と判断し、ドロップ状態に入ります。
よくある誤解は両者を「上限と下限のしきい値ペア」と捉えることです。正しくは、target は許容遅延の水準、interval はその超過がどれだけ続いたら本物とみなすかの時間窓です。target を下げれば遅延は浅くなるがバーストに弱くなり、interval を縮めれば反応は速くなるが偽陽性(健全バーストの誤ドロップ)が増えます。
ドロップ間隔がなぜ逆数平方根なのか
ドロップ状態に入ると、CoDel は 1 個落とすたびに次のドロップまでの間隔を詰めていきます。その縮め方が interval / sqrt(count) です(count は連続ドロップ回数)。
# CoDel デキュー時の擬似コード(要点)
sojourn = now - packet.enqueue_time
if sojourn < target or queue_bytes < MTU:
dropping = false # 健全。一時バーストは許す。first_above_time をリセット
else:
if first_above_time == 0:
first_above_time = now + interval # 超過の開始を記録
else if now >= first_above_time and not dropping:
dropping = true # interval だけ継続した。ドロップ開始
count = 1
drop_next = now + interval / sqrt(count)
while dropping and now >= drop_next:
drop(packet); packet = dequeue()
count += 1
drop_next = drop_next + interval / sqrt(count)
count が増えるほど次のドロップまでの間隔が逆数平方根で短くなり、輻輳が続くほどドロップ頻度が上がります。なぜ平方根なのか。これは TCP のスループットが損失率 p の平方根に反比例する(いわゆる 1/sqrt(p) 則)ことへの整合です。送信側のレートを一定割合だけ下げるには、ドロップ確率を二乗のペースで上げる必要があり、裏返せばドロップ間隔を平方根のペースで詰めれば送信側の cwnd が線形に下がります。線形制御の応答を AQM 側が時間軸で実現する仕組みです(AIMD と輻輳制御の数理 参照)。
逆にキューが捌けて滞留時間が target を下回ると dropping を解除します。ただし count は完全には捨てず控えめに減らして引き継ぐため、輻輳が短時間で再発したときに前回のペースから素早く反応できます。これにより、断続的な輻輳でゼロから立ち上げ直す無駄を避けています。
| 観点 | RED | CoDel |
|---|---|---|
| 制御変数 | 平均キュー長(バイト/パケット数) | デキュー時の実測滞留時間 |
| 設定パラメータ | min_th / max_th / max_p の手調整 | target と interval(時間の量) |
| 速度依存 | あり(環境ごとに再調整) | なし(時間基準で不変) |
| バースト吸収 | やや苦手(平均で鈍る) | 得意(interval 内の一時キューは許容) |
| 反応の起点 | しきい値超過で確率ドロップ | target を interval 継続超過で開始 |
FQ-CoDel — フロー分離との合わせ技
CoDel 単体は 1 本のキューを制御するだけで、フロー間の不公平には手を出しません。実運用で事実上の標準になっているのは FQ-CoDel(Flow Queueing + CoDel, RFC 8290)です。これは入ってきたパケットを 5-タプル(送信元/宛先 IP・ポート・プロトコル)のハッシュで多数のサブキュー(既定 1024 本)に振り分け、各サブキューを独立した CoDel インスタンスで管理したうえで、サブキュー間を**改良版 DRR(Deficit Round Robin)**で取り出します。基盤となるスケジューリングはパケットスケジューリングの DRR と同じ仕組みです。
効果は二重です。第一に、各フローが自前の CoDel と自前のキューを持つため、1 本の大量転送(エレファントフロー)が埋めるのは自分のサブキューだけで、他のフローの遅延を巻き込みません。CoDel 単体ではすべてのフローが同じキューを共有するので、この分離が決定的に効きます。
第二に、FQ-CoDel はサブキューを**新規(new)と継続(old)**の 2 グループに分け、新規フローを優先して取り出します。新しく現れた、あるいはしばらく無音だったフロー(DNS 問い合わせ、対話的な ACK、Web 接続の最初の数パケット)は、まだ自分の quantum を使い切っておらず new リストに入るため、継続中の大量転送より先に送られます。送り終えて quantum を使い切ると old へ移ります。
大量転送は自分のサブキューに長い CoDel キューを作りますが、それは他フローに波及しません。さらに音声パケットや SSH のキーストロークは低レートの new フローとして先回しされるため、たとえ回線が飽和していても対話的な応答性が保たれます。これが FQ-CoDel が家庭用ルータや Linux の既定キューイング規律として広く採用された理由です。
ECN との連携と設計上の含意
CoDel・FQ-CoDel とも、パケットを落とす代わりに ECN(明示的輻輳通知) でマークする変種を持ちます。ECN 対応フローには、ドロップと同じ判定タイミングで CE マークだけを付けられるため、再送遅延ゼロでキューを浅く保てます(ECN と L4S 参照)。ドロップとマークの判定ロジックは共通で、違いは「捨てるか合図するか」だけです。
設計上の急所は、CoDel がフローのレートを直接制御しない点です。CoDel はあくまでボトルネックの滞留時間を浅く保ち、ドロップ(またはマーク)という形で輻輳シグナルを早く返すだけで、減速するのはエンド間の輻輳制御側です。AQM とエンド輻輳制御は役割分担の関係にあり、片方だけでは完結しません。逆に言えば、CoDel が偽陽性で健全バーストを落とすとスループットを無駄に削るため、interval を最悪 RTT に合わせて余裕を持たせる設計が効いてきます。
「CoDel が RED と決定的に違う点は」と聞かれたら、「RED は環境依存のキュー長を見て調整が破綻したが、CoDel はリンク速度に依存しない滞留時間を見て、target を interval の間継続超過したときだけ落とすため実質無調整で動く」と即答できること。あわせて、ドロップ間隔が interval / sqrt(count) と逆数平方根で詰まるのは TCP スループットが損失率の平方根に反比例することへの整合である点、FQ-CoDel の優位はフロー分離(エレファント隔離)と new フロー優先(対話的応答性)の二点である点が頻出です。
ネットワーク Article
CoDel と FQ-CoDel:遅延を直接狙うAQMの原理を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
CoDel
比較で見る軸
難易度: advanced / カテゴリ: ネットワーク / タグ数: 5
導入後に効く点
target はバースト用の一時キューを許す下限、interval は最悪 RTT 相当の観測窓で、両者は時間の量なのでリンク速度に依存しない。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- ネットワーク
- タグ数
- 5
判断チェックリスト
- 自社の用途が「CoDel / FQ-CoDel」に近いか確認する。
- 強みである「CoDel はキュー長ではなくパケットの滞留時間(sojourn time)を測り、target が interval の間継続して超え続けたときだけドロップに入る。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。