feat(transport/cc): spurious congestion event detection
- Adds
CongestionControlStatsstruct withcongestion_eventsandspurious_congestion_eventsstats. - Passes
CongestionControlStatsdown to the congestion controller. - Adds mechanism to detect spurious congestion events by logging lost packet numbers in
ClassicCongestionControl::maybe_lost_packetsand cleaning them out as acks come in. Once all lost packets have been (late) acked a spurious congestion event is detected. The data structure is regularly purged from old packets where no late acks are expected anymore. - Logs those events in the added stats.
This is the first part of #2694 taking care of the detection mechanism and exposing stats. I will link a PR on the Firefox side to expose the stats as telemetry later.
The next step after this will be to implement the congestion event undo mechanism described in RFC 9438.
Some resources and other implementations of the same mechanisms:
- quinn has very promising numbers in their PR. The graphs also nicely show the congestion window recovery.
- msquic: This is where I got the
2*PTOmaximum age for lost packets from. Packets declared longer than2 * PTOago are purged from the list. - cloudflare quiche uses a different detection mechanism where they compare lost packet count to lost packet count before the congestion event and undo if it goes below a certain threshold (due to late acks). This is more aggressive, as it doesn't make sure all lost packets were actually spurious losses.
- it's hard to compare to what the linux kernel is doing as the detection mechanisms for TCP look very different than in QUIC, as is also described in RFC 9438 4.9.2.
Using this patch and running upload tests against [h3.speed.cloudflare.com] (RUST_LOG=neqo_bin=info cargo run --bin neqo-client -- -m=POST --stats --upload-size=300000000 https://h3.speed.cloudflare.com/) on my home network (which can generally be described as very flaky) I can actually already observe a good amount of spurious congestion events. Take e.g. this example output:
53.453 INFO stats for Client ...
rx: 147040 drop 3 dup 0 saved 3
tx: 215429 lost 47 lateack 22 ptoack 0 unackdrop 0 <--------- late acks observed here
cc: congestion_events 24 spurious_congestion_events 5 <-------- spurious congestion events here
pmtud: 4 sent 4 acked 0 lost 0 change 1500 iface_mtu 1500 pmtu
resumed: false
frames rx:
crypto 6 done 1 token 0 close 0
ack 147026 (max 215411) ping 5745 padding 0
stream 5 reset 0 stop 0
max: stream 0 data 224 stream_data 251
blocked: stream 0 data 0 stream_data 0
datagram 0
ncid 0 rcid 0 pchallenge 0 presponse 0
ack_frequency 0
frames tx:
crypto 4 done 0 token 0 close 1
ack 5787 (max 147906) ping 4 padding 4
stream 215250 reset 0 stop 0
max: stream 2 data 0 stream_data 0
blocked: stream 0 data 0 stream_data 250
datagram 0
ncid 0 rcid 0 pchallenge 0 presponse 0
ack_frequency 0
ecn:
tx:
Initial Count({NotEct: 4, Ect1: 0, Ect0: 0, Ce: 0})
Handshake Count({NotEct: 4, Ect1: 0, Ect0: 0, Ce: 0})
Short Count({NotEct: 215411, Ect1: 0, Ect0: 10, Ce: 0})
acked:
rx:
Initial Count({NotEct: 4, Ect1: 0, Ect0: 0, Ce: 0})
Handshake Count({NotEct: 3, Ect1: 0, Ect0: 0, Ce: 0})
Short Count({NotEct: 147030, Ect1: 0, Ect0: 0, Ce: 0})
path validation outcomes: ValidationCount({Capable: 0, NotCapable(BlackHole): 0, NotCapable(Bleaching): 1, NotCapable(ReceivedUnsentECT1): 0})
mark transitions:
dscp: Cs0: 147043
@mb had also observed similar spurious losses in the past when uploading to google drive and had documented his findings in #1472.
I think overall this points to the undo mechanism having big potential wins to our upload. Looking forward to see what the stats will show in the wild!
Codecov Report
:x: Patch coverage is 99.52153% with 1 line in your changes missing coverage. Please review.
:white_check_mark: Project coverage is 93.43%. Comparing base (6431a57) to head (f4776b5).
:warning: Report is 1 commits behind head on main.
Additional details and impacted files
@@ Coverage Diff @@
## main #3086 +/- ##
==========================================
- Coverage 93.44% 93.43% -0.01%
==========================================
Files 125 125
Lines 36409 36568 +159
Branches 36409 36568 +159
==========================================
+ Hits 34022 34168 +146
- Misses 1542 1555 +13
Partials 845 845
| Components | Coverage Δ | |
|---|---|---|
| neqo-common | 97.36% <ø> (ø) |
|
| neqo-crypto | 83.25% <ø> (-0.48%) |
:arrow_down: |
| neqo-http3 | 93.30% <ø> (ø) |
|
| neqo-qpack | 94.29% <ø> (ø) |
|
| neqo-transport | 94.55% <99.52%> (+0.03%) |
:arrow_up: |
| neqo-udp | 79.42% <ø> (ø) |
|
| mtu | 85.44% <ø> (ø) |
Bencher Report
| Branch | spurious_congestion_event_stats |
| Testbed | On-prem |
Click to view all benchmark results
| Benchmark | Latency | Benchmark Result nanoseconds (ns) (Result Δ%) | Upper Boundary nanoseconds (ns) (Limit %) |
|---|---|---|---|
| 1-conn/1-100mb-req/mtu-1504 (aka. Upload)/client | 📈 view plot 🚷 view threshold | 211,220,000.00 ns(+1.85%)Baseline: 207,378,646.08 ns | 216,970,652.87 ns (97.35%) |
| 1-conn/1-100mb-resp/mtu-1504 (aka. Download)/client | 📈 view plot 🚷 view threshold | 204,890,000.00 ns(+1.67%)Baseline: 201,527,790.97 ns | 211,553,782.52 ns (96.85%) |
| 1-conn/1-1b-resp/mtu-1504 (aka. HPS)/client | 📈 view plot 🚷 view threshold | 38,645,000.00 ns(+17.35%)Baseline: 32,930,408.55 ns | 44,915,534.72 ns (86.04%) |
| 1-conn/10_000-parallel-1b-resp/mtu-1504 (aka. RPS)/client | 📈 view plot 🚷 view threshold | 285,270,000.00 ns(-1.75%)Baseline: 290,347,790.97 ns | 303,176,422.32 ns (94.09%) |
| 1-streams/each-1000-bytes/simulated-time | 📈 view plot 🚷 view threshold | 118,970,000.00 ns(+0.19%)Baseline: 118,749,168.65 ns | 120,632,343.37 ns (98.62%) |
| 1-streams/each-1000-bytes/wallclock-time | 📈 view plot 🚷 view threshold | 582,020.00 ns(-1.56%)Baseline: 591,245.96 ns | 613,251.05 ns (94.91%) |
| 1000-streams/each-1-bytes/simulated-time | 📈 view plot 🚷 view threshold | 2,331,900,000.00 ns(-79.68%)Baseline: 11,476,738,242.28 ns | 24,723,680,121.99 ns (9.43%) |
| 1000-streams/each-1-bytes/wallclock-time | 📈 view plot 🚷 view threshold | 12,637,000.00 ns(-7.22%)Baseline: 13,620,631.83 ns | 15,198,761.26 ns (83.14%) |
| 1000-streams/each-1000-bytes/simulated-time | 📈 view plot 🚷 view threshold | 16,198,000,000.00 ns(-11.22%)Baseline: 18,244,983,372.92 ns | 20,967,483,132.19 ns (77.25%) |
| 1000-streams/each-1000-bytes/wallclock-time | 📈 view plot 🚷 view threshold | 51,061,000.00 ns(+0.75%)Baseline: 50,682,152.02 ns | 56,497,885.93 ns (90.38%) |
| RxStreamOrderer::inbound_frame() | 📈 view plot 🚷 view threshold | 109,880,000.00 ns(+0.14%)Baseline: 109,725,748.22 ns | 111,488,315.43 ns (98.56%) |
| coalesce_acked_from_zero 1+1 entries | 📈 view plot 🚷 view threshold | 89.18 ns(+0.21%)Baseline: 88.99 ns | 90.31 ns (98.75%) |
| coalesce_acked_from_zero 10+1 entries | 📈 view plot 🚷 view threshold | 105.78 ns(-0.26%)Baseline: 106.05 ns | 107.15 ns (98.72%) |
| coalesce_acked_from_zero 1000+1 entries | 📈 view plot 🚷 view threshold | 91.65 ns(+1.13%)Baseline: 90.63 ns | 95.17 ns (96.30%) |
| coalesce_acked_from_zero 3+1 entries | 📈 view plot 🚷 view threshold | 106.23 ns(-0.30%)Baseline: 106.55 ns | 107.59 ns (98.73%) |
| decode 1048576 bytes, mask 3f | 📈 view plot 🚷 view threshold | 1,759,400.00 ns(+6.04%)Baseline: 1,659,111.16 ns | 1,858,445.90 ns (94.67%) |
| decode 1048576 bytes, mask 7f | 📈 view plot 🚷 view threshold | 5,045,400.00 ns(-0.35%)Baseline: 5,062,949.88 ns | 5,109,507.60 ns (98.75%) |
| decode 1048576 bytes, mask ff | 📈 view plot 🚷 view threshold | 2,997,000.00 ns(-0.92%)Baseline: 3,024,775.77 ns | 3,056,993.76 ns (98.04%) |
| decode 4096 bytes, mask 3f | 📈 view plot 🚷 view threshold | 6,232.20 ns(-12.94%)Baseline: 7,158.47 ns | 10,069.88 ns (61.89%) |
| decode 4096 bytes, mask 7f | 📈 view plot 🚷 view threshold | 19,643.00 ns(-0.66%)Baseline: 19,773.05 ns | 20,392.58 ns (96.32%) |
| decode 4096 bytes, mask ff | 📈 view plot 🚷 view threshold | 11,403.00 ns(+0.34%)Baseline: 11,364.52 ns | 12,411.36 ns (91.88%) |
| sent::Packets::take_ranges | 📈 view plot 🚷 view threshold | 4,590.90 ns(-2.32%)Baseline: 4,700.02 ns | 4,947.57 ns (92.79%) |
| transfer/pacing-false/same-seed/simulated-time/run | 📈 view plot 🚷 view threshold | 25,234,000,000.00 ns(-0.57%)Baseline: 25,378,241,050.12 ns | 25,959,808,020.42 ns (97.20%) |
| transfer/pacing-false/same-seed/wallclock-time/run | 📈 view plot 🚷 view threshold | 25,020,000.00 ns(-1.96%)Baseline: 25,519,522.67 ns | 27,074,045.99 ns (92.41%) |
| transfer/pacing-false/varying-seeds/simulated-time/run | 📈 view plot 🚷 view threshold | 25,223,000,000.00 ns(+0.17%)Baseline: 25,179,293,556.09 ns | 25,230,887,220.31 ns (99.97%) |
| transfer/pacing-false/varying-seeds/wallclock-time/run | 📈 view plot 🚷 view threshold | 25,224,000.00 ns(-1.64%)Baseline: 25,645,417.66 ns | 27,273,306.51 ns (92.49%) |
| transfer/pacing-true/same-seed/simulated-time/run | 📈 view plot 🚷 view threshold | 25,069,000,000.00 ns(-1.66%)Baseline: 25,493,004,773.27 ns | 26,003,308,587.97 ns (96.41%) |
| transfer/pacing-true/same-seed/wallclock-time/run | 📈 view plot 🚷 view threshold | 25,559,000.00 ns(-4.65%)Baseline: 26,806,661.10 ns | 28,669,008.07 ns (89.15%) |
| transfer/pacing-true/varying-seeds/simulated-time/run | 📈 view plot 🚷 view threshold | 25,030,000,000.00 ns(+0.13%)Baseline: 24,996,513,126.49 ns | 25,045,319,775.20 ns (99.94%) |
| transfer/pacing-true/varying-seeds/wallclock-time/run | 📈 view plot 🚷 view threshold | 25,728,000.00 ns(-1.64%)Baseline: 26,157,644.39 ns | 27,833,172.24 ns (92.44%) |
Bencher Report
| Branch | spurious_congestion_event_stats |
| Testbed | On-prem |
Click to view all benchmark results
| Benchmark | Latency | Benchmark Result milliseconds (ms) (Result Δ%) | Upper Boundary milliseconds (ms) (Limit %) |
|---|---|---|---|
| google vs. neqo (cubic, paced) | 📈 view plot 🚷 view threshold | 278.82 ms(+0.11%)Baseline: 278.52 ms | 283.02 ms (98.52%) |
| Benchmark | Latency | Benchmark Result milliseconds (ms) (Result Δ%) | Upper Boundary milliseconds (ms) (Limit %) |
|---|---|---|---|
| msquic vs. neqo (cubic, paced) | 📈 view plot 🚷 view threshold | 221.57 ms(+10.40%)Baseline: 200.69 ms | 238.39 ms (92.94%) |
| Benchmark | Latency | Benchmark Result milliseconds (ms) (Result Δ%) | Upper Boundary milliseconds (ms) (Limit %) |
|---|---|---|---|
| neqo vs. google (cubic, paced) | 📈 view plot 🚷 view threshold | 757.79 ms(-0.43%)Baseline: 761.10 ms | 779.14 ms (97.26%) |
| Benchmark | Latency | Benchmark Result milliseconds (ms) (Result Δ%) | Upper Boundary milliseconds (ms) (Limit %) |
|---|---|---|---|
| neqo vs. msquic (cubic, paced) | 📈 view plot 🚷 view threshold | 156.41 ms(-0.76%)Baseline: 157.61 ms | 160.35 ms (97.54%) |
| Benchmark | Latency | Benchmark Result milliseconds (ms) (Result Δ%) | Upper Boundary milliseconds (ms) (Limit %) |
|---|---|---|---|
| neqo vs. neqo (cubic) | 📈 view plot 🚷 view threshold | 96.23 ms(+4.46%)Baseline: 92.12 ms | 97.83 ms (98.37%) |
| Benchmark | Latency | Benchmark Result milliseconds (ms) (Result Δ%) | Upper Boundary milliseconds (ms) (Limit %) |
|---|---|---|---|
| neqo vs. neqo (cubic, paced) | 📈 view plot 🚷 view threshold | 95.73 ms(+2.43%)Baseline: 93.46 ms | 99.03 ms (96.67%) |
| Benchmark | Latency | Benchmark Result milliseconds (ms) (Result Δ%) | Upper Boundary milliseconds (ms) (Limit %) |
|---|---|---|---|
| neqo vs. neqo (reno) | 📈 view plot 🚷 view threshold | 94.42 ms(+2.53%)Baseline: 92.10 ms | 97.67 ms (96.68%) |
| Benchmark | Latency | Benchmark Result milliseconds (ms) (Result Δ%) | Upper Boundary milliseconds (ms) (Limit %) |
|---|---|---|---|
| neqo vs. neqo (reno, paced) | 📈 view plot 🚷 view threshold | 96.56 ms(+3.45%)Baseline: 93.34 ms | 98.77 ms (97.77%) |
| Benchmark | Latency | Benchmark Result milliseconds (ms) (Result Δ%) | Upper Boundary milliseconds (ms) (Limit %) |
|---|---|---|---|
| neqo vs. quiche (cubic, paced) | 📈 view plot 🚷 view threshold | 192.77 ms(-0.44%)Baseline: 193.62 ms | 196.89 ms (97.91%) |
| Benchmark | Latency | Benchmark Result milliseconds (ms) (Result Δ%) | Upper Boundary milliseconds (ms) (Limit %) |
|---|---|---|---|
| neqo vs. s2n (cubic, paced) | 📈 view plot 🚷 view threshold | 220.92 ms(-0.12%)Baseline: 221.19 ms | 224.15 ms (98.56%) |
| Benchmark | Latency | Benchmark Result milliseconds (ms) (Result Δ%) | Upper Boundary milliseconds (ms) (Limit %) |
|---|---|---|---|
| quiche vs. neqo (cubic, paced) | 📈 view plot 🚷 view threshold | 156.72 ms(+2.26%)Baseline: 153.26 ms | 158.42 ms (98.93%) |
| Benchmark | Latency | Benchmark Result milliseconds (ms) (Result Δ%) | Upper Boundary milliseconds (ms) (Limit %) |
|---|---|---|---|
| s2n vs. neqo (cubic, paced) | 📈 view plot 🚷 view threshold | 171.78 ms(-1.06%)Baseline: 173.63 ms | 177.77 ms (96.63%) |
Minor comments.
Once you have been able to integrate this in Firefox and we see it working, I suggest we merge here. Sounds good?
Sounds good, I will get to the Firefox integration next week!
Added a TODO for potentially tracking spurious congestion events per congestion epoch.
I have this integrated in firefox now, but not sure how to best upload the patch because it includes vendoring in the neqo changes. If it's wanted I can upload a patch to Phabricator as WIP, otherwise I'll just do the proper patch once the neqo change is in nightly.
Anyways, I can see the following in https://debug-ping-preview.firebaseapp.com/pings/ after running a few simultaneous https://h3.speed.cloudflare.com speedtests:
"networking.http_3_spurious_congestion_event_ratio": {
"values": {
"0": 10,
"5000": 1 // Note the 10k scaling factor for glean metrics, so this is 50% spurious events observed for one connection
},
"sum": 5000
},
@mxinden can you do another review and approve if there is nothing left on your side so we can merge?
#3111 got me thinking:
Should we include ECN congestion events here? They cannot be spurious, so I think it is correct to only include congestion events due to loss in the ratio recorded in glean.
Should we then also expose another stat that counts congestion events due to ECN-CE? I assume just as lost only correlates to congestion_events the amount of ECN-CE marks that we observe is also not equal to the number of congestion events caused by it. This would give us the biggest granularity and enable us to record all kind of things to glean in neqo_glue (ECN-CE vs loss ratio, spurious vs all cc events, spurious vs loss cc events, ...).
I think that would be best, but not sure if it should happen in this PR. I don't think it is much more complexity regarding review, but it's only barely related...
And while thinking about this I found an actual bug in this code: I recorded congestion events twice (in on_packets_lost and on_congestion_event). Will wait with a fix for the above discussion as it depends on what we decide here.
My suggestion would be to record loss based cc events here:
https://github.com/mozilla/neqo/blob/6d56ec96fc97b488b0d67df1e72807386fdb23dd/neqo-transport/src/cc/classic_cc.rs#L311
And ECN-CE based cc events here:
https://github.com/mozilla/neqo/blob/6d56ec96fc97b488b0d67df1e72807386fdb23dd/neqo-transport/src/cc/classic_cc.rs#L332-L334
@mxinden So hold off with the review I asked for above for now, but please share your thoughts on this when you get to it.
Should we include ECN congestion events here? They cannot be spurious
Good observation! I would not include ECN when considering whether congestion events were spurious - they are a confirmed congestion event, albeit possibly at lower intensity (so might warrant a special reaction).
Should we then also expose another stat that counts congestion events due to ECN-CE?
Yes.
Above sounds good!
For ECN CE, we currently only have:
https://glam.telemetry.mozilla.org/fog/probe/networking_http_3_ecn_ce_ect0_ratio_received/explore?
I implemented the above in the latest commit.
We now have the stats congestion_events_due_to_ecn and congestion_events_due_to_loss and can then decide in neqo_glue on the Firefox side how exactly we want to collect those in metrics.
Also fixed the double count I had before and added a check for congestion_events_due_to_ecn counting in the existing classic_cc::tests::ecn_ce test.
If you're interested, another data point from my weird home network that sees a lot of spurious loss:
RUST_LOG=neqo_bin=info cargo run --bin neqo-client -- -m=POST --stats --upload-size=100000000 https://h3.speed.cloudflare.com/ --qlog-dir .
tx: 72112 lost 69 lateack 41 ptoack 15 unackdrop 0
cc: loss_congestion_events 11 ecn_congestion_events 0 spurious_congestion_events 5
And the following qvis congestion graph:
Can really see the impact spurious congestion events have here. We basically don't spend any time in slowstart because we immediately have some spurious loss and then we're never able to really converge to a congestion point over the whole 100MB upload.
Exciting to see this happening in a real network, i.e. your home network.
Did you already start the integration into Firefox? I am curious whether you see this behavior when doing normal web browsing.
I had already done the integration into firefox and tested it locally, but just from the office and not with normal web browsing. With this new version I will compile again and use it for a bit the next time I'm working from home (which might not be this week though...).
That said I addressed the comments in https://github.com/mozilla/neqo/pull/3086/commits/16dae83efe4859216648e09cb1a8e00224909e23, so I think this should be ready for another round of reviews :)
CodSpeed Performance Report
Merging #3086 will degrade performances by 4.38%
Comparing omansfeld:spurious_congestion_event_stats (f4776b5) with main (6431a57)
Summary
⚡ 2 improvements
❌ 1 regression
✅ 20 untouched
:warning: Please fix the performance issues or acknowledge them on CodSpeed.
Benchmarks breakdown
| Mode | Benchmark | BASE |
HEAD |
Change | |
|---|---|---|---|---|---|
| ⚡ | Simulation | client |
853.6 ms | 771.9 ms | +10.59% |
| ⚡ | Simulation | wallclock-time |
1.1 ms | 1 ms | +3.67% |
| ❌ | Simulation | run |
163 ms | 170.4 ms | -4.38% |
Failed Interop Tests
QUIC Interop Runner, client vs. server, differences relative to 5be8510e7461597c9d45449655e60265d2ff222e.
neqo-latest as client
- neqo-latest vs. aioquic: A
- neqo-latest vs. go-x-net: A BP BA
- neqo-latest vs. haproxy: :rocket:~~M~~ A L1 BP BA
- neqo-latest vs. kwik: :warning:L1 BP BA
- neqo-latest vs. linuxquic: A L1
- neqo-latest vs. lsquic: L1 C1
- neqo-latest vs. msquic: :rocket:~~R~~ :warning:Z A L1 C1
- neqo-latest vs. mvfst: A L1
- neqo-latest vs. nginx: A :warning:L1 C1 BP BA
- neqo-latest vs. ngtcp2: A :warning:L1 C1 CM
- neqo-latest vs. picoquic: :warning:Z A L1 C1
- neqo-latest vs. quic-go: A
- neqo-latest vs. quiche: A :rocket:~~L1~~ :warning:C1 BP BA
- neqo-latest vs. quinn: A L1
- neqo-latest vs. s2n-quic: A :rocket:~~L1 C1~~ :warning:BP BA CM
- neqo-latest vs. tquic: S A :warning:C1 BP BA
- neqo-latest vs. xquic: :warning:A C1
neqo-latest as server
- aioquic vs. neqo-latest: :warning:L1 CM
- chrome vs. neqo-latest: 3
- go-x-net vs. neqo-latest: CM
- kwik vs. neqo-latest: BP BA CM
- linuxquic vs. neqo-latest: :warning:BP
- lsquic vs. neqo-latest: :rocket:~~C1~~ :warning:BA
- msquic vs. neqo-latest: CM
- mvfst vs. neqo-latest: Z A L1 C1 CM
- openssl vs. neqo-latest: LR M A CM
- quic-go vs. neqo-latest: :warning:L1 CM
- quiche vs. neqo-latest: :rocket:~~C1~~ CM
- quinn vs. neqo-latest: V2 CM
- s2n-quic vs. neqo-latest: :warning:BP CM
- tquic vs. neqo-latest: CM
- xquic vs. neqo-latest: M :warning:C1 CM
All results
Succeeded Interop Tests
QUIC Interop Runner, client vs. server
neqo-latest as client
- neqo-latest vs. aioquic: H DC LR C20 M S R Z 3 B U L1 L2 C1 C2 6 V2 BP BA
- neqo-latest vs. go-x-net: H DC LR M B U L2 C2 6
- neqo-latest vs. haproxy: H DC LR C20 :rocket:~~M~~ S R Z 3 B U L2 C1 C2 6 V2
- neqo-latest vs. kwik: H DC LR C20 M S R Z 3 B U A :warning:L1 L2 C1 C2 6 V2
- neqo-latest vs. linuxquic: H DC LR C20 M S R Z 3 B U E L2 C1 C2 6 V2 BP BA CM
- neqo-latest vs. lsquic: H DC LR C20 M S R Z 3 B U E A L2 C2 6 V2 BP BA CM
- neqo-latest vs. msquic: H DC LR C20 M S :warning:Z :rocket:~~R~~ B U L2 C2 6 V2 BP BA
- neqo-latest vs. mvfst: H DC LR M R Z 3 B U L2 C1 C2 6 BP BA
- neqo-latest vs. neqo: H DC LR C20 M S R Z 3 B U E A L1 L2 C1 C2 6 V2 BP BA CM
- neqo-latest vs. neqo-latest: H DC LR C20 M S R Z 3 B U E A L1 L2 C1 C2 6 V2 BP BA CM
- neqo-latest vs. nginx: H DC LR C20 M S R Z 3 B U :warning:L1 L2 :warning:C1 C2 6
- neqo-latest vs. ngtcp2: H DC LR C20 M S R Z 3 B U E :warning:L1 L2 :warning:C1 C2 6 V2 BP BA
- neqo-latest vs. picoquic: H DC LR C20 M S R :warning:Z 3 B U E L2 C2 6 V2 BP BA
- neqo-latest vs. quic-go: H DC LR C20 M S R Z 3 B U L1 L2 C1 C2 6 BP BA
- neqo-latest vs. quiche: H DC LR C20 M S R Z 3 B U :rocket:~~L1~~ L2 :warning:C1 C2 6
- neqo-latest vs. quinn: H DC LR C20 M S R Z 3 B U E L2 C1 C2 6 BP BA
- neqo-latest vs. s2n-quic: H DC LR C20 M S R 3 B U E :rocket:~~L1~~ L2 :rocket:~~C1~~ C2 6 :warning:BP
- neqo-latest vs. tquic: H DC LR C20 M R Z 3 B U L1 L2 :warning:C1 C2 6
- neqo-latest vs. xquic: :rocket:~~H DC LR C20 M S R Z 3 B U L1 L2 C2 6 BP BA~~
neqo-latest as server
- aioquic vs. neqo-latest: H DC LR C20 M S R Z 3 B U A :warning:L1 L2 C1 C2 6 V2 BP BA
- go-x-net vs. neqo-latest: H DC LR M B U A L2 C2 6 BP BA
- kwik vs. neqo-latest: H DC LR C20 M S R Z 3 B U A L1 L2 C1 C2 6 V2
- linuxquic vs. neqo-latest: H DC LR C20 M S R Z 3 B U E A L1 L2 C1 C2 6 V2 :warning:BP BA CM
- lsquic vs. neqo-latest: H DC LR C20 M S R Z 3 B E A L1 L2 :rocket:~~C1~~ C2 6 V2 BP :warning:BA CM
- msquic vs. neqo-latest: H DC LR C20 M S R Z B U A L1 L2 C1 C2 6 V2 BP BA
- mvfst vs. neqo-latest: H DC LR M 3 B L2 C2 6 BP BA
- neqo vs. neqo-latest: H DC LR C20 M S R Z 3 B U E A L1 L2 C1 C2 6 V2 BP BA CM
- ngtcp2 vs. neqo-latest: H DC LR C20 M S R Z 3 B U E A L1 L2 C1 C2 6 V2 BP BA CM
- openssl vs. neqo-latest: H DC C20 S R 3 B L2 C2 6 BP BA
- picoquic vs. neqo-latest: H DC LR C20 M S R Z 3 B U E A L1 L2 C1 C2 6 V2 BP BA CM
- quic-go vs. neqo-latest: H DC LR C20 M S R Z 3 B U A :warning:L1 L2 C1 C2 6 BP BA
- quiche vs. neqo-latest: H DC LR M S R Z 3 B A L1 L2 :rocket:~~C1~~ C2 6 BP BA
- quinn vs. neqo-latest: H DC LR C20 M S R Z 3 B U E A L1 L2 C1 C2 6 BP BA
- s2n-quic vs. neqo-latest: H DC LR M S R 3 B E A L1 L2 C1 C2 6 :warning:BP BA
- tquic vs. neqo-latest: H DC LR M S R Z 3 B A L1 L2 C1 C2 6 BP BA
- xquic vs. neqo-latest: H DC LR C20 S R Z 3 B U A L1 L2 :warning:C1 C2 6 BP BA
Unsupported Interop Tests
QUIC Interop Runner, client vs. server
neqo-latest as client
- neqo-latest vs. aioquic: E CM
- neqo-latest vs. go-x-net: C20 S R Z 3 E L1 C1 V2 CM
- neqo-latest vs. haproxy: E CM
- neqo-latest vs. kwik: E CM
- neqo-latest vs. msquic: 3 E CM
- neqo-latest vs. mvfst: C20 S E V2 CM
- neqo-latest vs. nginx: E V2 CM
- neqo-latest vs. picoquic: CM
- neqo-latest vs. quic-go: E V2 CM
- neqo-latest vs. quiche: E V2 CM
- neqo-latest vs. quinn: V2 CM
- neqo-latest vs. s2n-quic: Z V2
- neqo-latest vs. tquic: E V2 CM
- neqo-latest vs. xquic: E V2 CM
neqo-latest as server
- aioquic vs. neqo-latest: E
- chrome vs. neqo-latest: H DC LR C20 M S R Z B U E A L1 L2 C1 C2 6 V2 BP BA CM
- go-x-net vs. neqo-latest: C20 S R Z 3 E L1 C1 V2
- kwik vs. neqo-latest: E
- lsquic vs. neqo-latest: U
- msquic vs. neqo-latest: 3 E
- mvfst vs. neqo-latest: C20 S R U E V2
- openssl vs. neqo-latest: Z U E L1 C1 V2
- quic-go vs. neqo-latest: E V2
- quiche vs. neqo-latest: C20 U E V2
- s2n-quic vs. neqo-latest: C20 Z U V2
- tquic vs. neqo-latest: C20 U E V2
- xquic vs. neqo-latest: E V2
Benchmark results
Performance differences relative to 6431a57a7bbce5db5c44d40b169982a79ce08e6d.
1-conn/1-100mb-resp/mtu-1504 (aka. Download)/client: No change in performance detected.
time: [204.54 ms 204.89 ms 205.32 ms]
thrpt: [487.04 MiB/s 488.06 MiB/s 488.90 MiB/s]
change:
time: [-0.1005% +0.1718% +0.4459%] (p = 0.21 > 0.05)
thrpt: [-0.4440% -0.1715% +0.1006%]
Found 6 outliers among 100 measurements (6.00%)
3 (3.00%) low mild
2 (2.00%) high mild
1 (1.00%) high severe
1-conn/10_000-parallel-1b-resp/mtu-1504 (aka. RPS)/client: Change within noise threshold.
time: [283.44 ms 285.27 ms 287.16 ms]
thrpt: [34.824 Kelem/s 35.055 Kelem/s 35.281 Kelem/s]
change:
time: [+0.6622% +1.5491% +2.4770%] (p = 0.00 -1.5255% -0.6579%]
Found 1 outliers among 100 measurements (1.00%)
1 (1.00%) high mild
1-conn/1-1b-resp/mtu-1504 (aka. HPS)/client: No change in performance detected.
time: [38.494 ms 38.645 ms 38.815 ms]
thrpt: [25.763 B/s 25.877 B/s 25.978 B/s]
change:
time: [-0.8712% -0.2417% +0.3695%] (p = 0.45 > 0.05)
thrpt: [-0.3681% +0.2423% +0.8789%]
Found 5 outliers among 100 measurements (5.00%)
1 (1.00%) high mild
4 (4.00%) high severe
1-conn/1-100mb-req/mtu-1504 (aka. Upload)/client: Change within noise threshold.
time: [210.96 ms 211.22 ms 211.49 ms]
thrpt: [472.84 MiB/s 473.44 MiB/s 474.02 MiB/s]
change:
time: [+0.9916% +1.1464% +1.2964%] (p = 0.00 -1.1334% -0.9819%]
Found 3 outliers among 100 measurements (3.00%)
1 (1.00%) low mild
2 (2.00%) high mild
decode 4096 bytes, mask ff: No change in performance detected.
time: [11.336 µs 11.403 µs 11.489 µs]
change: [-0.2655% +0.5651% +1.4326%] (p = 0.25 > 0.05)
Found 19 outliers among 100 measurements (19.00%)
4 (4.00%) low mild
1 (1.00%) high mild
14 (14.00%) high severe
decode 1048576 bytes, mask ff: No change in performance detected.
time: [2.9894 ms 2.9970 ms 3.0064 ms]
change: [-0.8768% -0.3053% +0.2327%] (p = 0.30 > 0.05)
Found 6 outliers among 100 measurements (6.00%)
6 (6.00%) high severe
decode 4096 bytes, mask 7f: No change in performance detected.
time: [19.588 µs 19.643 µs 19.705 µs]
change: [-0.2540% +0.0759% +0.3907%] (p = 0.65 > 0.05)
Found 17 outliers among 100 measurements (17.00%)
2 (2.00%) low mild
2 (2.00%) high mild
13 (13.00%) high severe
decode 1048576 bytes, mask 7f: No change in performance detected.
time: [5.0309 ms 5.0454 ms 5.0627 ms]
change: [-0.5423% -0.0871% +0.3420%] (p = 0.72 > 0.05)
Found 13 outliers among 100 measurements (13.00%)
13 (13.00%) high severe
decode 4096 bytes, mask 3f: No change in performance detected.
time: [6.2039 µs 6.2322 µs 6.2667 µs]
change: [-0.4368% +0.4382% +1.5428%] (p = 0.44 > 0.05)
Found 14 outliers among 100 measurements (14.00%)
4 (4.00%) low mild
1 (1.00%) high mild
9 (9.00%) high severe
decode 1048576 bytes, mask 3f: No change in performance detected.
time: [1.7579 ms 1.7594 ms 1.7623 ms]
change: [-0.2229% +0.0113% +0.2396%] (p = 0.83 > 0.05)
Found 5 outliers among 100 measurements (5.00%)
4 (4.00%) high mild
1 (1.00%) high severe
1-streams/each-1000-bytes/wallclock-time: No change in performance detected.
time: [579.64 µs 582.02 µs 584.80 µs]
change: [-0.8642% -0.2012% +0.4908%] (p = 0.57 > 0.05)
Found 8 outliers among 100 measurements (8.00%)
8 (8.00%) high severe
1-streams/each-1000-bytes/simulated-time
time: [118.75 ms 118.97 ms 119.18 ms]
thrpt: [8.1938 KiB/s 8.2086 KiB/s 8.2234 KiB/s]
change:
time: [-0.3230% -0.0640% +0.1945%] (p = 0.63 > 0.05)
thrpt: [-0.1941% +0.0641% +0.3240%]
No change in performance detected.
1000-streams/each-1-bytes/wallclock-time: Change within noise threshold.
time: [12.594 ms 12.637 ms 12.681 ms]
change: [+0.6082% +1.1776% +1.7220%] (p = 0.00 1000-streams/each-1-bytes/simulated-time: No change in performance detected.
time: [2.3284 s 2.3319 s 2.3354 s]
thrpt: [428.19 B/s 428.83 B/s 429.48 B/s]
change:
time: [-0.3275% -0.1033% +0.1098%] (p = 0.35 > 0.05)
thrpt: [-0.1097% +0.1034% +0.3286%]
1000-streams/each-1000-bytes/wallclock-time: :broken_heart: Performance has regressed.
time: [50.950 ms 51.061 ms 51.173 ms]
change: [+1.9588% +2.3039% +2.6499%] (p = 0.00 1000-streams/each-1000-bytes/simulated-time: No change in performance detected.
time: [15.945 s 16.198 s 16.457 s]
thrpt: [59.340 KiB/s 60.289 KiB/s 61.247 KiB/s]
change:
time: [-1.8468% +0.2709% +2.5420%] (p = 0.81 > 0.05)
thrpt: [-2.4790% -0.2702% +1.8816%]
coalesce_acked_from_zero 1+1 entries: No change in performance detected.
time: [88.912 ns 89.179 ns 89.449 ns]
change: [-0.6760% -0.2681% +0.1186%] (p = 0.19 > 0.05)
Found 6 outliers among 100 measurements (6.00%)
6 (6.00%) high mild
coalesce_acked_from_zero 3+1 entries: No change in performance detected.
time: [105.85 ns 106.23 ns 106.64 ns]
change: [-0.5632% +0.0053% +0.7377%] (p = 0.99 > 0.05)
Found 13 outliers among 100 measurements (13.00%)
1 (1.00%) low mild
1 (1.00%) high mild
11 (11.00%) high severe
coalesce_acked_from_zero 10+1 entries: No change in performance detected.
time: [105.34 ns 105.78 ns 106.34 ns]
change: [-0.3891% +0.0943% +0.6477%] (p = 0.74 > 0.05)
Found 11 outliers among 100 measurements (11.00%)
5 (5.00%) low mild
1 (1.00%) high mild
5 (5.00%) high severe
coalesce_acked_from_zero 1000+1 entries: No change in performance detected.
time: [91.560 ns 91.654 ns 91.762 ns]
change: [-0.5207% +0.0925% +0.7317%] (p = 0.77 > 0.05)
Found 15 outliers among 100 measurements (15.00%)
5 (5.00%) high mild
10 (10.00%) high severe
RxStreamOrderer::inbound_frame(): No change in performance detected.
time: [109.67 ms 109.88 ms 110.18 ms]
change: [-0.0151% +0.1956% +0.4719%] (p = 0.14 > 0.05)
Found 3 outliers among 100 measurements (3.00%)
2 (2.00%) low mild
1 (1.00%) high severe
sent::Packets::take_ranges: No change in performance detected.
time: [4.4755 µs 4.5909 µs 4.7040 µs]
change: [-3.7743% -0.7540% +2.3738%] (p = 0.64 > 0.05)
Found 2 outliers among 100 measurements (2.00%)
2 (2.00%) high mild
transfer/pacing-false/varying-seeds/wallclock-time/run: Change within noise threshold.
time: [25.187 ms 25.224 ms 25.262 ms]
change: [-1.1451% -0.8982% -0.6511%] (p = 0.00 Found 1 outliers among 100 measurements (1.00%)
1 (1.00%) high mildtransfer/pacing-false/varying-seeds/simulated-time/run: No change in performance detected.
time: [25.185 s 25.223 s 25.261 s]
thrpt: [162.15 KiB/s 162.39 KiB/s 162.64 KiB/s]
change:
time: [-0.0440% +0.1682% +0.3869%] (p = 0.13 > 0.05)
thrpt: [-0.3854% -0.1679% +0.0440%]
transfer/pacing-true/varying-seeds/wallclock-time/run: Change within noise threshold.
time: [25.664 ms 25.728 ms 25.793 ms]
change: [+0.3262% +0.6891% +1.0616%] (p = 0.00 Found 2 outliers among 100 measurements (2.00%)
1 (1.00%) low mild
1 (1.00%) high mildtransfer/pacing-true/varying-seeds/simulated-time/run: No change in performance detected.
time: [24.991 s 25.030 s 25.070 s]
thrpt: [163.39 KiB/s 163.65 KiB/s 163.90 KiB/s]
change:
time: [-0.2125% -0.0054% +0.2054%] (p = 0.96 > 0.05)
thrpt: [-0.2050% +0.0054% +0.2129%]
Found 4 outliers among 100 measurements (4.00%)
1 (1.00%) low mild
3 (3.00%) high mild
transfer/pacing-false/same-seed/wallclock-time/run: Change within noise threshold.
time: [24.992 ms 25.020 ms 25.058 ms]
change: [+0.8796% +1.0368% +1.2116%] (p = 0.00 Found 5 outliers among 100 measurements (5.00%)
1 (1.00%) low mild
3 (3.00%) high mild
1 (1.00%) high severetransfer/pacing-false/same-seed/simulated-time/run: No change in performance detected.
time: [25.234 s 25.234 s 25.234 s]
thrpt: [162.32 KiB/s 162.32 KiB/s 162.32 KiB/s]
change:
time: [+0.0000% +0.0000% +0.0000%] (p = NaN > 0.05)
thrpt: [+0.0000% +0.0000% +0.0000%]
transfer/pacing-true/same-seed/wallclock-time/run: Change within noise threshold.
time: [25.526 ms 25.559 ms 25.608 ms]
change: [-1.6526% -1.4114% -1.1653%] (p = 0.00 Found 4 outliers among 100 measurements (4.00%)
3 (3.00%) high mild
1 (1.00%) high severetransfer/pacing-true/same-seed/simulated-time/run: No change in performance detected.
time: [25.069 s 25.069 s 25.069 s]
thrpt: [163.39 KiB/s 163.39 KiB/s 163.39 KiB/s]
change:
time: [+0.0000% +0.0000% +0.0000%] (p = NaN > 0.05)
thrpt: [+0.0000% +0.0000% +0.0000%]
Download data for profiler.firefox.com or download performance comparison data.
Client/server transfer results
Performance differences relative to 6431a57a7bbce5db5c44d40b169982a79ce08e6d.
Transfer of 33554432 bytes over loopback, min. 100 runs. All unit-less numbers are in milliseconds.
| Client vs. server (params) | Mean ± σ | Min | Max | MiB/s ± σ | Δ main |
Δ main |
|---|---|---|---|---|---|---|
| google vs. google | 455.2 ± 3.9 | 450.4 | 471.5 | 70.3 ± 8.2 | ||
| google vs. neqo (cubic, paced) | 278.8 ± 4.4 | 269.8 | 294.0 | 114.8 ± 7.3 | -0.7 | -0.2% |
| msquic vs. msquic | 191.8 ± 84.2 | 137.7 | 617.3 | 166.8 ± 0.4 | ||
| msquic vs. neqo (cubic, paced) | 221.6 ± 71.2 | 155.4 | 612.3 | 144.4 ± 0.4 | 2.5 | 1.2% |
| neqo vs. google (cubic, paced) | 757.8 ± 5.0 | 751.0 | 780.7 | 42.2 ± 6.4 | 1.3 | 0.2% |
| neqo vs. msquic (cubic, paced) | 156.4 ± 4.1 | 150.8 | 168.6 | 204.6 ± 7.8 | -0.0 | -0.0% |
| neqo vs. neqo (cubic) | 96.2 ± 4.3 | 87.9 | 105.8 | 332.5 ± 7.4 | :broken_heart: 1.9 | 2.0% |
| neqo vs. neqo (cubic, paced) | 95.7 ± 4.4 | 87.4 | 104.5 | 334.3 ± 7.3 | :green_heart: -1.9 | -1.9% |
| neqo vs. neqo (reno) | 94.4 ± 3.9 | 88.6 | 104.8 | 338.9 ± 8.2 | -0.1 | -0.2% |
| neqo vs. neqo (reno, paced) | 96.6 ± 4.7 | 88.0 | 107.7 | 331.4 ± 6.8 | 0.8 | 0.8% |
| neqo vs. quiche (cubic, paced) | 192.8 ± 4.8 | 185.9 | 207.2 | 166.0 ± 6.7 | -0.6 | -0.3% |
| neqo vs. s2n (cubic, paced) | 220.9 ± 4.0 | 213.6 | 228.2 | 144.8 ± 8.0 | -1.2 | -0.5% |
| quiche vs. neqo (cubic, paced) | 156.7 ± 6.8 | 142.9 | 196.0 | 204.2 ± 4.7 | :broken_heart: 3.2 | 2.1% |
| quiche vs. quiche | 146.3 ± 4.9 | 139.2 | 159.3 | 218.8 ± 6.5 | ||
| s2n vs. neqo (cubic, paced) | 171.8 ± 4.9 | 162.8 | 191.3 | 186.3 ± 6.5 | -0.5 | -0.3% |
| s2n vs. s2n | 247.7 ± 24.8 | 232.6 | 345.3 | 129.2 ± 1.3 |
Download data for profiler.firefox.com or download performance comparison data.