feat(proto): replace BBR with BBRv3 + breaking Controller API changes#611
Conversation
c737f72 to
032ebef
Compare
Performance Comparison Report
|
| Scenario | noq | upstream | Delta | CPU (avg/max) |
|---|---|---|---|---|
| large-single | 5525.2 Mbps | 8019.0 Mbps | -31.1% | 96.7% / 148.0% |
| medium-concurrent | 5484.9 Mbps | 7593.0 Mbps | -27.8% | 97.0% / 150.0% |
| medium-single | 3807.3 Mbps | 4766.9 Mbps | -20.1% | 92.6% / 101.0% |
| small-concurrent | 3885.2 Mbps | 5450.7 Mbps | -28.7% | 94.8% / 102.0% |
| small-single | 3627.8 Mbps | 4931.0 Mbps | -26.4% | 96.9% / 153.0% |
Netsim Benchmarks (network simulation)
| Condition | noq | upstream | Delta |
|---|---|---|---|
| ideal | N/A | 4107.1 Mbps | N/A |
| lan | N/A | 810.4 Mbps | N/A |
| lossy | N/A | 55.9 Mbps | N/A |
| wan | N/A | 83.8 Mbps | N/A |
Summary
noq is 27.4% slower on average
acc3302245eff6e951f138511f3159216d9e7a88 - artifacts
No results available
69c68b87b1db92ea798b96e49bd916afbfddee22 - artifacts
Raw Benchmarks (localhost)
| Scenario | noq | upstream | Delta | CPU (avg/max) |
|---|---|---|---|---|
| large-single | 5671.4 Mbps | 7965.0 Mbps | -28.8% | 98.3% / 151.0% |
| medium-concurrent | 5471.5 Mbps | 7700.6 Mbps | -28.9% | 97.8% / 151.0% |
| medium-single | 4344.6 Mbps | 4749.7 Mbps | -8.5% | 93.2% / 102.0% |
| small-concurrent | 3930.5 Mbps | 5335.9 Mbps | -26.3% | 96.1% / 103.0% |
| small-single | 3620.8 Mbps | 4695.3 Mbps | -22.9% | 97.3% / 153.0% |
Netsim Benchmarks (network simulation)
| Condition | noq | upstream | Delta |
|---|---|---|---|
| ideal | 3114.7 Mbps | N/A | N/A |
| lan | 782.4 Mbps | N/A | N/A |
| lossy | 69.8 Mbps | N/A | N/A |
| wan | 83.8 Mbps | N/A | N/A |
Summary
noq is 24.3% slower on average
|
Documentation for this PR has been generated and is available at: https://n0-computer.github.io/noq/pr/611/docs/noq/ Last updated: 2026-04-24T08:35:31Z |
Pulls in upstream quinn PR quinn-rs/quinn#2481 Squashes the four upstream commits and adapts them to noq's multipath divergence Closes 603 Upstream commits - f8b25b24b - granular packet events for congestion controllers - 52f04ed9b - Pacer queries Controller for pacing rate / send quantum - c022e308a - BBRv3 implementation + MaxFilter - ce60e5b5c - remove old BBR implementation Breaking changes to `Controller` trait: - `on_ack` gains `pn: u64` - `on_congestion_event` gains `largest_lost: u64` - New optional `on_packet_sent`, `on_packet_lost`, `on_ack_frequency_update` - New `metrics()` -> `ControllerMetrics { congestion_window, ssthresh, pacing_rate, send_quantum }`, used by the pacer Pacer changes: - `Pacer::delay()` now also takes `capacity: Option<u64>` and `pacing_rate: Option<u64>` from the controller; CC-supplied values take precedence over the window-derived defaults but are still capped by noq's `max_bytes_per_second`. Co-authored-by: Tipuch <fiorini751@proton.me>
032ebef to
8b7b168
Compare
flub
left a comment
There was a problem hiding this comment.
Overall LGTM, I'd like to tidy up some variable names mostly. We already started diverging from Quinn on those and would be best to stay consistent. I didn't see any problem wrt multipath or other noq-specific things though.
| self.process_ecn(now, space, path, newly_acked.len() as u64, ecn, sent); | ||
| let pn_space = self.spaces[space].for_path(path); | ||
| let sent = pn_space.largest_acked_packet_sent; | ||
| let largest_sent = pn_space.largest_acked_packet.unwrap(); |
There was a problem hiding this comment.
I'm not particularly happy with this unwrap. IIUC this works because new_largest is true and therefore the code above just inserted it. It should be at least an expect() with a message to that effect. But maybe there's a better way. E.g. new_largest could be an Option<pn> maybe, so that this if becomes an if let Some(pn) (not tried this)?
Aside: these variable names are also pretty bad, I had to look them up to make sense of this. I even came very close to improve them before, but failed to find a good alternative. This time I can come up with a (hopefully) sensible suggestion though:
PacketNumberSpace::largest_acked_packet_pn(waslargest_acked_packet)PacketNumberSpace::largest_acked_packet_send_time(waslargest_acked_packet_sent)
But I'm fine if you'd rather not mix that in this PR. OTOH I won't complain if you do.
| newly_acked: u64, | ||
| ecn: frame::EcnCounts, | ||
| largest_sent_time: Instant, | ||
| largest_sent: u64, |
There was a problem hiding this comment.
nit: largest_sent_pn would be better. And, err, I won't mind if you tidy up newly_acked into newly_acked_pn.
| now: Instant, | ||
| sent: Instant, | ||
| bytes: u64, | ||
| pn: u64, |
There was a problem hiding this comment.
Hum, I realise this is probably upstream... but let's improve naming because our experience so far has shown that these names do get confusing and consistency helps. I think @matheus23 advocated for pn before over packet_number and that seems like it would be the right compromise for the variables here. So:
on_sent(..., largest_pn: u64)on_packet_sent(..., pn: u64)on_ack(..., pn: u64)on_congestion_event(..., largest_lost_pn: u64)on_packet_lost(..., pn: u64, ...)
And also use the same variable names in all the impls.
| // Not timing-aware, so it's safe to call this for inferred acks, such as arise from | ||
| // high-latency handshakes | ||
| fn on_packet_acked(&mut self, now: Instant, path_id: PathId, info: SentPacket) { | ||
| fn on_packet_acked(&mut self, now: Instant, path_id: PathId, packet: u64, info: SentPacket) { |
Pulls in upstream quinn PR quinn-rs/quinn#2481
Squashes the upstream commits and adapts them.
Closes #603
Upstream commits
Breaking Changes
Controllertrait:on_ackgainspn: u64on_congestion_eventgainslargest_lost: u64on_packet_sent,on_packet_lost,on_ack_frequency_updatemetrics()->ControllerMetrics { congestion_window, ssthresh, pacing_rate, send_quantum }, used by the pacerPacer::delay()now also takescapacity: Option<u64>andpacing_rate: Option<u64>from the controller; CC-supplied values take precedence over the window-derived defaults but are still capped by noq'smax_bytes_per_second.