Skip to content

Normalize congestion-control growth across packet sizes - #686

Merged
kazuho merged 5 commits into
kazuho/timeless-cubicfrom
kazuho/normalize-cc-mtu
Aug 24, 2026
Merged

Normalize congestion-control growth across packet sizes#686
kazuho merged 5 commits into
kazuho/timeless-cubicfrom
kazuho/normalize-cc-mtu

Conversation

@kazuho

@kazuho kazuho commented Aug 24, 2026

Copy link
Copy Markdown
Member

Builds on top of #678, PR against #678.

Quicly’s default max_udp_payload_size is 1280, which is smaller than the TCP packet size commonly used on paths with an IP MTU of 1500. When running as a server, quicly adopts max(default, sizeof(packet_received)), therefore its MTU might be slightly higher than 1280. But since quicly does not implement DPLPMTUD, the size does not grow beyond that.

Reno and CUBIC congestion-avoidance growth depends on packet size. A flow using 1280 therefore grows less aggressively in byte terms and can receive less throughput when competing with TCP flows using larger packets.

Correcting this in the congestion controller follows RFC 7141 §2.3, which says that when equal bit rates are desired for flows using different packet sizes, the transport algorithm should be changed rather than network equipment.

This PR follows that advice. For an IP MTU of 1500, the maximum UDP payload is 1472 bytes over IPv4 and 1452 bytes over IPv6. Because the congestion controller does not know the IP version, their midpoint—1462 bytes—is used as the reference maximum UDP payload size.

CWND still increases in increments of the connection’s actual max_udp_payload_size; the reference size only controls the rate of congestion-avoidance growth.

Normalization is enabled in the default contexts and is respected by Reno, CUBIC, and Cuback. Pico and cubic-legacy retain their existing behavior.

Applications can disable normalization through normalize_cc_mtu. The CLI provides --no-normalize-cc-mtu, and the simulator uses -M as the opt-out.

Evaluation

For each network profile, two long-lived flows using the same congestion controller competed with each other:

  • The baseline flow used max_udp_payload_size=1462.
  • The experiment flow used max_udp_payload_size=1280.

The experiment was run with normalization disabled and enabled for the 1280-byte flow. Normalization does not change the baseline flow’s growth because its max_udp_payload_size equals the reference size.

Each experiment group contained 20 runs: ten sub-RTT start phases in both flow orders. Measurements covered at least six estimated congestion-avoidance periods after at least two periods of warm-up. No runs were filtered.

The benchmark used the 15 network profiles in t/simulator-plot.rb. The table shows the geometric mean of (T_{1280}/T_{1462}) across the profile-level means:

Controller 1280 without normalization 1280 with normalization
CUBIC 0.9201 (−7.99%), GSD ×1.0725 0.9911 (−0.89%), GSD ×1.0571
Cuback 0.9462 (−5.38%), GSD ×1.0649 1.0034 (+0.34%), GSD ×1.0540

Without normalization, the 1280-byte flow suffered a 5–8% aggregate throughput disadvantage relative to the 1462-byte baseline. Normalization removed that disadvantage.

Note on Packet Admission Policy

For the above evaluation, the simulator used the packet-size-independent tail-drop admission introduced by commit 1ae7d8f, following RFC 7141 §4.2.1.2.

A bottleneck queue that admits every packet whenever that packet fits may give more bandwidth to flows using smaller MTUs, because their packets can use residual queue space unavailable to larger packets. As RFC 7141 points out, this is unfairness in the bottleneck’s queue admission; not a property or fault of the congestion-control algorithm.

The simulator’s -B option restores this byte-fit admission behavior for reproducing or studying such networks.

kazuho added 5 commits August 24, 2026 08:40
…anges command option, API, and implements that in reno only
Reserve queue space based on the largest configured packet, preventing smaller packets from consuming residual space unavailable to larger packets, as recommended by RFC 7141 section 4.2.1.2.

Add -B to retain byte-fit admission for comparison.
Enable normalization in the spec and performant contexts, and replace the CLI and simulator enable switches with explicit opt-outs.
@kazuho
kazuho merged commit b472826 into kazuho/timeless-cubic Aug 24, 2026
8 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant