Skip to content

feat(transport/cc): make slow start pluggable and add hystart++#3278

Merged
larseggert merged 19 commits intomozilla:mainfrom
omansfeld:pluggable_slow_start
Mar 13, 2026
Merged

feat(transport/cc): make slow start pluggable and add hystart++#3278
larseggert merged 19 commits intomozilla:mainfrom
omansfeld:pluggable_slow_start

Conversation

@omansfeld
Copy link
Copy Markdown
Collaborator

@omansfeld omansfeld commented Dec 16, 2025

fdc7597 introduces the scaffolding to make slow start configurable by introducing a SlowStart trait and using a generic parameter with that trait bound in the ClassicCongestionControl struct. It also rewrites the current slow start implementation in classic_slow_start.rs.

df81bdb makes the slow start algorithm configurable at run-time via the CLI/ConnectionParams, similar to how it works for Cubic/NewReno.

As a reminder: fdc7597..df81bdb had already had a review cycle before I started working on HyStart++ and made the PR a draft again.

41e8c04 implements HyStart++ as per RFC 9406 with extensive tests added in 86b496d.

With the latest commit ae92b97 I re-architected the SlowStart trait and the order in which slow start functionality is called from the congestion controller. Hystart functionality is unchanged between that commit and the former implementation, this just improves architecture and makes it more extensible for future work.

It now separates functionality into the correct order of operations for slow start (check for exit -> do the exit -> apply growth if not exiting -> do congestion avoidance if not in slow start anymore) which also makes it easier to add things like SUSS or rapid-start that accelerate slow start growth (but not touch the exit heuristics) in the future.

To maybe make review easier, the main parts are:

  • neqo-transport/src/cc/hystart.rs contains the HyStart++ implementation.
  • neqo-transport/src/cc/tests/hystart.rs contains tests for the HyStart++ implementation.
  • neqo-transport/src/cc/classic_slow_start.rs has the standard slow start implementation that was previously hardcoded into classic_cc.rs.
  • neqo-transport/src/cc/classic_cc.rs has the core on_packets_acked flow that calls everything else and the trait definition for SlowStart. There is also a small change to on_packet_sent for round-tracking.
  • ... and then there is a lot of changing function calls in tests, scaffolding, etc.

I think it might make it the easiest to look at classic_cc.rs first and then follow the code flow from there into the HyStart++ implementation. I'm not sure it makes a lot of sense to look at commits one-by-one because of the re-architecture in the latest commit.

I also have some qlogs from validating the implementation against h3.speed.cloudflare.com where one can see HyStart++ working as intended. See this qvis screenshot where it exits at the same window size that is later converged on during congestion avoidance:

hystart_textbook-exit

Copilot AI review requested due to automatic review settings December 16, 2025 14:04
Comment thread neqo-transport/src/cc/tests/cubic.rs
Comment thread neqo-transport/src/cc/tests/new_reno.rs
@codecov
Copy link
Copy Markdown

codecov Bot commented Dec 16, 2025

Codecov Report

❌ Patch coverage is 95.11278% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.00%. Comparing base (70a0ba6) to head (b356f8c).
⚠️ Report is 6 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3278      +/-   ##
==========================================
- Coverage   94.12%   94.00%   -0.13%     
==========================================
  Files         125      131       +6     
  Lines       38339    38811     +472     
  Branches    38339    38811     +472     
==========================================
+ Hits        36086    36483     +397     
- Misses       1412     1481      +69     
- Partials      841      847       +6     
Flag Coverage Δ
freebsd 93.03% <92.10%> (-0.12%) ⬇️
linux 94.10% <95.11%> (+<0.01%) ⬆️
macos 93.98% <95.11%> (-0.02%) ⬇️
windows 94.09% <95.11%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
neqo-common 98.54% <ø> (ø)
neqo-crypto 86.90% <ø> (ø)
neqo-http3 93.91% <ø> (ø)
neqo-qpack 94.34% <ø> (ø)
neqo-transport 94.92% <95.11%> (-0.05%) ⬇️
neqo-udp 82.97% <ø> (ø)
mtu 86.61% <ø> (ø)

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@github-actions
Copy link
Copy Markdown
Contributor

Failed Interop Tests

QUIC Interop Runner, client vs. server, differences relative to main at b3338f9.

neqo-pr as clientneqo-pr as server
neqo-pr vs. aioquic: A 🚀C1
neqo-pr vs. go-x-net: A BP BA
neqo-pr vs. haproxy: A BP BA
neqo-pr vs. kwik: BP BA
neqo-pr vs. linuxquic: A 🚀L1 C1
neqo-pr vs. lsquic: L1 C1
neqo-pr vs. msquic: A L1 ⚠️L2 C1
neqo-pr vs. mvfst: A ⚠️L1 C1
neqo-pr vs. nginx: A ⚠️L1 BP BA
neqo-pr vs. ngtcp2: A CM
neqo-pr vs. picoquic: A ⚠️L1
neqo-pr vs. quic-go: A L1
neqo-pr vs. quiche: A ⚠️L1 C1 BP BA
neqo-pr vs. quinn: A L1 🚀C1
neqo-pr vs. s2n-quic: A ⚠️L1 C1 🚀BP BA CM
neqo-pr vs. tquic: S A L1 BP BA
neqo-pr vs. xquic: A 🚀L1 C1
aioquic vs. neqo-pr: ⚠️L1 CM
go-x-net vs. neqo-pr: CM
kwik vs. neqo-pr: BP BA CM
msquic vs. neqo-pr: ⚠️L1 CM
mvfst vs. neqo-pr: Z A L1 C1 CM
openssl vs. neqo-pr: LR M A CM
quic-go vs. neqo-pr: CM
quiche vs. neqo-pr: ⚠️C1 CM
quinn vs. neqo-pr: V2 CM
s2n-quic vs. neqo-pr: CM
tquic vs. neqo-pr: CM
xquic vs. neqo-pr: M CM
All results

Succeeded Interop Tests

QUIC Interop Runner, client vs. server

neqo-pr as client

neqo-pr as server

Unsupported Interop Tests

QUIC Interop Runner, client vs. server

neqo-pr as client

neqo-pr as server

@omansfeld omansfeld requested a review from Copilot December 16, 2025 15:29
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Comment thread neqo-transport/src/cc/classic_cc.rs Outdated
Comment thread neqo-transport/src/cc/classic_slow_start.rs Outdated
Copy link
Copy Markdown
Member

@mxinden mxinden left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In favor of the design itself.

I suggest only merging here once we have more than one slow start algorithm. Each pull request should improve the state of main. Merging here now only adds a dead abstraction, i.e. SlowStart, only implemented once. (Similar to trait CongestionControl today.)

@omansfeld
Copy link
Copy Markdown
Collaborator Author

In favor of the design itself.

I suggest only merging here once we have more than one slow start algorithm. Each pull request should improve the state of main. Merging here now only adds a dead abstraction, i.e. SlowStart, only implemented once. (Similar to trait CongestionControl today.)

I very much get that point. That said, what would you say is the best point to move forward with this work then?

Having a bunch of dependent PRs seems like it could easily become a rebasing nightmare and one big PR seems like it could become very cumbersome to review. I would also very much like to have the along-the-way feedback of incremental PRs.

Should this be reviewed and merged onto a feature-branch that then eventually gets merged onto main?

Comment thread neqo-transport/src/cc/tests/cubic.rs Outdated
Comment thread neqo-transport/src/cc/classic_cc.rs Outdated
Comment thread neqo-transport/src/cc/classic_slow_start.rs
Comment thread neqo-transport/src/cc/classic_slow_start.rs Outdated
Comment thread neqo-transport/src/cc/classic_slow_start.rs Outdated
@omansfeld omansfeld changed the title refactor(transport/cc): make slow start pluggable feat(transport/cc): make slow start pluggable and add hystart++ Dec 19, 2025
@omansfeld omansfeld marked this pull request as draft December 19, 2025 20:39
@omansfeld
Copy link
Copy Markdown
Collaborator Author

I suggest only merging here once we have more than one slow start algorithm. Each pull request should improve the state of main. Merging here now only adds a dead abstraction, i.e. SlowStart, only implemented once. (Similar to trait CongestionControl today.)

As per @mxinden's comment I made this PR a draft again and will also add HyStart++ as part of it. I might still ask for review on single commits along the way to have some incremental feedback.

@omansfeld omansfeld force-pushed the pluggable_slow_start branch 2 times, most recently from ba2537b to 36308ef Compare December 19, 2025 21:58
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Dec 19, 2025

Merging this PR will degrade performance by 12.71%

⚡ 2 improved benchmarks
❌ 3 regressed benchmarks
✅ 46 untouched benchmarks

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
WallTime neqo-s2n 510.5 ms 584.8 ms -12.71%
WallTime quiche-neqo 345.8 ms 358.2 ms -3.45%
WallTime msquic-msquic 482.2 ms 459.3 ms +4.98%
WallTime neqo-neqo-newreno-nopacing 280 ms 292.1 ms -4.14%
WallTime neqo-neqo-cubic-nopacing 283.4 ms 274.4 ms +3.28%

Comparing omansfeld:pluggable_slow_start (b356f8c) with main (70a0ba6)

Open in CodSpeed

@omansfeld omansfeld requested a review from mxinden December 23, 2025 11:53
@omansfeld
Copy link
Copy Markdown
Collaborator Author

I added another commit to make slow start configurable via connection parameters and the CLI. Would welcome another review once you have some time @mxinden!

Also a question, just to make sure I understood the parameter code structure correctly:

ConnectionParameters is what actually sets the cc parameters for the sender and is also what we would set via prefs from Firefox for e.g. experimentation in neqo_glue, correct?

Self {
cc: match conn_params.get_cc_algorithm() {
CongestionControlAlgorithm::NewReno => {
Box::new(ClassicCongestionControl::new(NewReno::default(), pmtud))
}
CongestionControlAlgorithm::Cubic => {
Box::new(ClassicCongestionControl::new(Cubic::default(), pmtud))
}
},

But QuicParameters is used to set CLI flags in which case it passes them on to ConnectionParams when a client/server is created (of course it also contains more than just connection parameters).

neqo/neqo-bin/src/lib.rs

Lines 234 to 243 in adcd4bd

pub fn get(&self, alpn: &str) -> ConnectionParameters {
let mut params = ConnectionParameters::default()
.max_streams(StreamType::BiDi, self.max_streams_bidi)
.max_streams(StreamType::UniDi, self.max_streams_uni)
.idle_timeout(Duration::from_secs(self.idle_timeout))
.initial_rtt(Duration::from_millis(self.initial_rtt_ms))
.cc_algorithm(self.congestion_control)
.pacing(!self.no_pacing)
.pmtud(!self.no_pmtud)
.sni_slicing(!self.no_sni_slicing);

But from Firefox we directly set our ConnectionParameters, so any defaults we have in Neqo basically don't matter if the consumer is Firefox, correct?

@mxinden
Copy link
Copy Markdown
Member

mxinden commented Dec 23, 2025

Also a question, just to make sure I understood the parameter code structure correctly:

ConnectionParameters is what actually sets the cc parameters for the sender and is also what we would set via prefs from Firefox for e.g. experimentation in neqo_glue, correct?

correct

But QuicParameters is used to set CLI flags in which case it passes them on to ConnectionParams when a client/server is created (of course it also contains more than just connection parameters).

correct

But from Firefox we directly set our ConnectionParameters, so any defaults we have in Neqo basically don't matter if the consumer is Firefox, correct?

neqo_glue does not use QuicParameters. neqo_glue does use ConnectionParameters::default.

Copy link
Copy Markdown
Member

@mxinden mxinden left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes here thus far look good to me.

@omansfeld omansfeld force-pushed the pluggable_slow_start branch 2 times, most recently from beb57b7 to 86b496d Compare February 18, 2026 15:03
@omansfeld omansfeld marked this pull request as ready for review February 20, 2026 16:55
@omansfeld
Copy link
Copy Markdown
Collaborator Author

The HyStart++ implementation is done now, so I'm opening up for review! I updated the main PR description to resemble the current state.

- there was a bug where we would exit css, but the rtt was still above
  the threshold for entering css, so we re-entered on the same ack
- now we either are in css and can exit, or we are not in css and can
  enter, but not go back and forth in a single ack
- this is in line with the RFC and with what other implementations are
  doing (msquic, linux, freeBSD)
- this results in slightly more aggressive growth, since now we have
  non-css growth on the ack that exits css and only go back into css on
  the next ack if the css exit was truly spurious
@omansfeld omansfeld force-pushed the pluggable_slow_start branch from 4fde4f7 to 6d0022b Compare March 5, 2026 18:51
Copy link
Copy Markdown
Member

@mxinden mxinden left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes. I will take another look.

Comment thread neqo-transport/src/cc/tests/mod.rs Outdated
Comment thread neqo-transport/src/cc/classic_cc.rs
Comment thread neqo-transport/src/sender.rs Outdated
Comment thread neqo-transport/src/cc/tests/hystart.rs Outdated
@mxinden
Copy link
Copy Markdown
Member

mxinden commented Mar 5, 2026

After in person discussions Max and I decided to not restrict HyStart++ to initial slow start only for the following reasons:

* simplifies the code in `classic_cc::on_packets_acked`

* RFC is only stating a SHOULD, so we are not in direct conflict

* after persistent congestion the previously established `ssthresh` might still be too high, so there might be loss before it is reached, thus HyStart++ still makes sense

Can you document this decision in the code? Sorry in case it is already documented and I missed it.

@omansfeld
Copy link
Copy Markdown
Collaborator Author

omansfeld commented Mar 6, 2026

Updated again in 89a5169. Addressed nits from @mxinden, added comment about the non-initial slow start decision and added SlowStart::reset() to erase state after persistent congestion. Also added another test that asserts this.

Copy link
Copy Markdown
Member

@mxinden mxinden left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ready to merge from my end.

I will take another look tomorrow with a fresh mind, but that doesn't need to block here.

Comment thread neqo-transport/src/cc/classic_cc.rs Outdated
@github-actions
Copy link
Copy Markdown
Contributor

Failed Interop Tests

QUIC Interop Runner, client vs. server, differences relative to main at 70a0ba6.

neqo-pr as clientneqo-pr as server
neqo-pr vs. go-x-net: BP BA
neqo-pr vs. haproxy: 🚀M BP BA
neqo-pr vs. kwik: BP BA
neqo-pr vs. lsquic: baseline result missing
neqo-pr vs. msquic: A L1 C1
neqo-pr vs. mvfst: A L1 C1 BA
neqo-pr vs. neqo: Z A
neqo-pr vs. nginx: BP BA
neqo-pr vs. ngtcp2: CM
neqo-pr vs. picoquic: A ⚠️BP BA
neqo-pr vs. quic-go: A
neqo-pr vs. quiche: L1 BP BA
neqo-pr vs. s2n-quic: 🚀BP BA CM
neqo-pr vs. tquic: S BP BA
neqo-pr vs. xquic: A 🚀C1
aioquic vs. neqo-pr: Z ⚠️L1 CM
go-x-net vs. neqo-pr: CM
kwik vs. neqo-pr: Z BP BA CM
lsquic vs. neqo-pr: Z 🚀C1 ⚠️BP
msquic vs. neqo-pr: Z ⚠️C1 CM
mvfst vs. neqo-pr: Z A L1 C1 CM
neqo vs. neqo-pr: Z A ⚠️BA
openssl vs. neqo-pr: LR M A BA CM
picoquic vs. neqo-pr: Z CM
quic-go vs. neqo-pr: ⚠️Z CM
quiche vs. neqo-pr: Z CM
quinn vs. neqo-pr: Z V2 CM
s2n-quic vs. neqo-pr: ⚠️B L1 CM
tquic vs. neqo-pr: Z CM
xquic vs. neqo-pr: M CM
All results

Succeeded Interop Tests

QUIC Interop Runner, client vs. server

neqo-pr as client

neqo-pr as server

Unsupported Interop Tests

QUIC Interop Runner, client vs. server

neqo-pr as client

neqo-pr as server

@github-actions
Copy link
Copy Markdown
Contributor

Client/server transfer results

Performance differences relative to 70a0ba6.

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
neqo-neqo-cubic-nopacing 96.7 ± 4.2 89.5 105.2 330.9 ± 7.6 💔 1.7 1.8%
neqo-s2n-cubic 219.4 ± 4.2 211.7 228.6 145.9 ± 7.6 💔 2.2 1.0%
s2n-neqo-cubic 173.2 ± 5.3 162.8 197.4 184.8 ± 6.0 💚 -2.1 -1.2%

Table above only shows statistically significant changes. See all results below.

All results

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-google-nopacing 456.4 ± 4.8 450.8 477.3 70.1 ± 6.7
google-neqo-cubic 275.0 ± 4.4 268.6 292.7 116.3 ± 7.3 -0.9 -0.3%
msquic-msquic-nopacing 232.5 ± 103.5 138.7 578.9 137.7 ± 0.3
msquic-neqo-cubic 217.3 ± 78.2 150.1 581.7 147.2 ± 0.4 -14.9 -6.4%
neqo-google-cubic 754.5 ± 4.8 748.5 781.8 42.4 ± 6.7 -0.0 -0.0%
neqo-msquic-cubic 159.6 ± 4.1 153.6 167.6 200.5 ± 7.8 0.9 0.5%
neqo-neqo-cubic 98.2 ± 4.4 88.7 107.4 325.8 ± 7.3 1.0 1.0%
neqo-neqo-cubic-nopacing 96.7 ± 4.2 89.5 105.2 330.9 ± 7.6 💔 1.7 1.8%
neqo-neqo-newreno 97.9 ± 4.9 86.7 108.3 326.7 ± 6.5 -0.0 -0.0%
neqo-neqo-newreno-nopacing 95.5 ± 3.9 88.6 103.5 334.9 ± 8.2 0.3 0.3%
neqo-quiche-cubic 190.5 ± 3.8 185.6 202.5 167.9 ± 8.4 -0.8 -0.4%
neqo-s2n-cubic 219.4 ± 4.2 211.7 228.6 145.9 ± 7.6 💔 2.2 1.0%
quiche-neqo-cubic 182.8 ± 7.5 171.8 221.6 175.0 ± 4.3 1.3 0.7%
quiche-quiche-nopacing 143.3 ± 4.6 136.8 155.8 223.3 ± 7.0
s2n-neqo-cubic 173.2 ± 5.3 162.8 197.4 184.8 ± 6.0 💚 -2.1 -1.2%
s2n-s2n-nopacing 247.9 ± 24.9 231.5 346.4 129.1 ± 1.3

Download data for profiler.firefox.com or download performance comparison data.

@github-actions
Copy link
Copy Markdown
Contributor

Benchmark results

Significant performance differences relative to 70a0ba6.

transfer/1-conn/1-100mb-resp (aka. Download)/mtu-1504: 💔 Performance has regressed by +1.7538%.
       time:   [201.36 ms 201.96 ms 202.67 ms]
       thrpt:  [493.42 MiB/s 495.15 MiB/s 496.63 MiB/s]
change:
       time:   [+1.3976% +1.7538% +2.1063] (p = 0.00 < 0.05)
       thrpt:  [-2.0629% -1.7236% -1.3784]
       Performance has regressed.
Found 5 outliers among 100 measurements (5.00%)
3 (3.00%) high mild
2 (2.00%) high severe
transfer/1-conn/1-100mb-req (aka. Upload)/mtu-1504: 💔 Performance has regressed by +1.9825%.
       time:   [205.70 ms 206.09 ms 206.52 ms]
       thrpt:  [484.22 MiB/s 485.22 MiB/s 486.14 MiB/s]
change:
       time:   [+1.6768% +1.9825% +2.2961] (p = 0.00 < 0.05)
       thrpt:  [-2.2445% -1.9439% -1.6492]
       Performance has regressed.
Found 3 outliers among 100 measurements (3.00%)
1 (1.00%) low mild
1 (1.00%) high mild
1 (1.00%) high severe
streams/walltime/1000-streams/each-1000-bytes: 💔 Performance has regressed by +4.0151%.
       time:   [46.323 ms 46.386 ms 46.459 ms]
       change: [+3.8356% +4.0151% +4.2065] (p = 0.00 < 0.05)
       Performance has regressed.
Found 2 outliers among 100 measurements (2.00%)
1 (1.00%) high mild
1 (1.00%) high severe
All results
transfer/1-conn/1-100mb-resp (aka. Download)/mtu-1504: 💔 Performance has regressed by +1.7538%.
       time:   [201.36 ms 201.96 ms 202.67 ms]
       thrpt:  [493.42 MiB/s 495.15 MiB/s 496.63 MiB/s]
change:
       time:   [+1.3976% +1.7538% +2.1063] (p = 0.00 < 0.05)
       thrpt:  [-2.0629% -1.7236% -1.3784]
       Performance has regressed.
Found 5 outliers among 100 measurements (5.00%)
3 (3.00%) high mild
2 (2.00%) high severe
transfer/1-conn/10_000-parallel-1b-resp (aka. RPS)/mtu-1504: Change within noise threshold.
       time:   [280.19 ms 282.00 ms 283.81 ms]
       thrpt:  [35.235 Kelem/s 35.462 Kelem/s 35.690 Kelem/s]
change:
       time:   [-1.9902% -1.0605% -0.1034] (p = 0.02 < 0.05)
       thrpt:  [+0.1035% +1.0719% +2.0306]
       Change within noise threshold.
transfer/1-conn/1-1b-resp (aka. HPS)/mtu-1504: No change in performance detected.
       time:   [38.585 ms 38.745 ms 38.923 ms]
       thrpt:  [25.692   B/s 25.810   B/s 25.917   B/s]
change:
       time:   [-0.3016% +0.2723% +0.8239] (p = 0.37 > 0.05)
       thrpt:  [-0.8171% -0.2715% +0.3025]
       No change in performance detected.
Found 10 outliers among 100 measurements (10.00%)
4 (4.00%) high mild
6 (6.00%) high severe
transfer/1-conn/1-100mb-req (aka. Upload)/mtu-1504: 💔 Performance has regressed by +1.9825%.
       time:   [205.70 ms 206.09 ms 206.52 ms]
       thrpt:  [484.22 MiB/s 485.22 MiB/s 486.14 MiB/s]
change:
       time:   [+1.6768% +1.9825% +2.2961] (p = 0.00 < 0.05)
       thrpt:  [-2.2445% -1.9439% -1.6492]
       Performance has regressed.
Found 3 outliers among 100 measurements (3.00%)
1 (1.00%) low mild
1 (1.00%) high mild
1 (1.00%) high severe
decode 4096 bytes, mask ff: No change in performance detected.
       time:   [5.5696 µs 5.5794 µs 5.5903 µs]
       change: [-0.7636% -0.1624% +0.3988] (p = 0.63 > 0.05)
       No change in performance detected.
Found 18 outliers among 100 measurements (18.00%)
9 (9.00%) high mild
9 (9.00%) high severe
decode 1048576 bytes, mask ff: No change in performance detected.
       time:   [1.4285 ms 1.4339 ms 1.4426 ms]
       change: [+0.0206% +0.4687% +1.1248] (p = 0.08 > 0.05)
       No change in performance detected.
Found 2 outliers among 100 measurements (2.00%)
1 (1.00%) high mild
1 (1.00%) high severe
decode 4096 bytes, mask 7f: No change in performance detected.
       time:   [8.4654 µs 8.4788 µs 8.4933 µs]
       change: [-0.3905% -0.0249% +0.2978] (p = 0.89 > 0.05)
       No change in performance detected.
Found 5 outliers among 100 measurements (5.00%)
4 (4.00%) high mild
1 (1.00%) high severe
decode 1048576 bytes, mask 7f: No change in performance detected.
       time:   [2.1760 ms 2.1837 ms 2.1962 ms]
       change: [-0.2251% +0.1621% +0.8014] (p = 0.62 > 0.05)
       No change in performance detected.
Found 1 outliers among 100 measurements (1.00%)
1 (1.00%) high severe
decode 4096 bytes, mask 3f: No change in performance detected.
       time:   [6.9147 µs 6.9251 µs 6.9363 µs]
       change: [-0.1618% +0.1666% +0.4920] (p = 0.33 > 0.05)
       No change in performance detected.
Found 5 outliers among 100 measurements (5.00%)
2 (2.00%) high mild
3 (3.00%) high severe
decode 1048576 bytes, mask 3f: No change in performance detected.
       time:   [1.7668 ms 1.7741 ms 1.7859 ms]
       change: [-0.0929% +0.3650% +1.0351] (p = 0.26 > 0.05)
       No change in performance detected.
Found 1 outliers among 100 measurements (1.00%)
1 (1.00%) high severe
streams/simulated/1-streams/each-1000-bytes: No change in performance detected.
       time:   [129.68 ms 129.68 ms 129.69 ms]
       thrpt:  [7.5302 KiB/s 7.5304 KiB/s 7.5306 KiB/s]
change:
       time:   [-0.0047% -0.0010% +0.0029] (p = 0.63 > 0.05)
       thrpt:  [-0.0029% +0.0010% +0.0047]
       No change in performance detected.
Found 1 outliers among 100 measurements (1.00%)
1 (1.00%) high mild
streams/simulated/1000-streams/each-1-bytes: Change within noise threshold.
       time:   [2.5364 s 2.5366 s 2.5369 s]
       thrpt:  [394.18   B/s 394.22   B/s 394.27   B/s]
change:
       time:   [+0.0013% +0.0174% +0.0334] (p = 0.04 < 0.05)
       thrpt:  [-0.0334% -0.0174% -0.0013]
       Change within noise threshold.
streams/simulated/1000-streams/each-1000-bytes: No change in performance detected.
       time:   [6.5817 s 6.5877 s 6.5946 s]
       thrpt:  [148.08 KiB/s 148.24 KiB/s 148.38 KiB/s]
change:
       time:   [-0.3384% -0.1509% +0.0312] (p = 0.12 > 0.05)
       thrpt:  [-0.0312% +0.1511% +0.3395]
       No change in performance detected.
Found 2 outliers among 100 measurements (2.00%)
2 (2.00%) high severe
streams/walltime/1-streams/each-1000-bytes: Change within noise threshold.
       time:   [587.68 µs 589.96 µs 592.79 µs]
       change: [+0.5995% +1.1178% +1.6935] (p = 0.00 < 0.05)
       Change within noise threshold.
Found 8 outliers among 100 measurements (8.00%)
1 (1.00%) high mild
7 (7.00%) high severe
streams/walltime/1000-streams/each-1-bytes: Change within noise threshold.
       time:   [12.406 ms 12.423 ms 12.441 ms]
       change: [-0.6380% -0.4222% -0.2021] (p = 0.00 < 0.05)
       Change within noise threshold.
streams/walltime/1000-streams/each-1000-bytes: 💔 Performance has regressed by +4.0151%.
       time:   [46.323 ms 46.386 ms 46.459 ms]
       change: [+3.8356% +4.0151% +4.2065] (p = 0.00 < 0.05)
       Performance has regressed.
Found 2 outliers among 100 measurements (2.00%)
1 (1.00%) high mild
1 (1.00%) high severe
coalesce_acked_from_zero 1+1 entries: No change in performance detected.
       time:   [92.552 ns 92.830 ns 93.104 ns]
       change: [-1.2295% -0.2166% +0.5790] (p = 0.69 > 0.05)
       No change in performance detected.
Found 10 outliers among 100 measurements (10.00%)
7 (7.00%) high mild
3 (3.00%) high severe
coalesce_acked_from_zero 3+1 entries: No change in performance detected.
       time:   [110.35 ns 110.68 ns 111.02 ns]
       change: [-3.4883% -1.0878% +0.3176] (p = 0.47 > 0.05)
       No change in performance detected.
Found 11 outliers among 100 measurements (11.00%)
2 (2.00%) high mild
9 (9.00%) high severe
coalesce_acked_from_zero 10+1 entries: No change in performance detected.
       time:   [109.93 ns 110.39 ns 110.91 ns]
       change: [-0.3048% +0.3136% +0.9892] (p = 0.36 > 0.05)
       No change in performance detected.
Found 12 outliers among 100 measurements (12.00%)
5 (5.00%) low mild
7 (7.00%) high severe
coalesce_acked_from_zero 1000+1 entries: No change in performance detected.
       time:   [94.568 ns 94.947 ns 95.587 ns]
       change: [-0.1114% +0.4768% +1.0125] (p = 0.11 > 0.05)
       No change in performance detected.
Found 10 outliers among 100 measurements (10.00%)
3 (3.00%) high mild
7 (7.00%) high severe
RxStreamOrderer::inbound_frame(): Change within noise threshold.
       time:   [108.09 ms 108.19 ms 108.29 ms]
       change: [-0.6652% -0.5509% -0.4337] (p = 0.00 < 0.05)
       Change within noise threshold.
Found 1 outliers among 100 measurements (1.00%)
1 (1.00%) high mild
sent::Packets::take_ranges: No change in performance detected.
       time:   [4.5181 µs 4.6285 µs 4.7448 µs]
       change: [-3.0163% +0.3531% +3.8585] (p = 0.85 > 0.05)
       No change in performance detected.
Found 3 outliers among 100 measurements (3.00%)
3 (3.00%) high mild
transfer/simulated/pacing-false/varying-seeds: No change in performance detected.
       time:   [23.941 s 23.941 s 23.941 s]
       thrpt:  [171.09 KiB/s 171.09 KiB/s 171.09 KiB/s]
change:
       time:   [+0.0000% +0.0000% +0.0000] (p = NaN > 0.05)
       thrpt:  [+0.0000% +0.0000% +0.0000]
       No change in performance detected.
transfer/simulated/pacing-true/varying-seeds: No change in performance detected.
       time:   [23.676 s 23.676 s 23.676 s]
       thrpt:  [173.01 KiB/s 173.01 KiB/s 173.01 KiB/s]
change:
       time:   [+0.0000% +0.0000% +0.0000] (p = NaN > 0.05)
       thrpt:  [+0.0000% +0.0000% +0.0000]
       No change in performance detected.
transfer/simulated/pacing-false/same-seed: No change in performance detected.
       time:   [23.941 s 23.941 s 23.941 s]
       thrpt:  [171.09 KiB/s 171.09 KiB/s 171.09 KiB/s]
change:
       time:   [+0.0000% +0.0000% +0.0000] (p = NaN > 0.05)
       thrpt:  [+0.0000% +0.0000% +0.0000]
       No change in performance detected.
transfer/simulated/pacing-true/same-seed: No change in performance detected.
       time:   [23.676 s 23.676 s 23.676 s]
       thrpt:  [173.01 KiB/s 173.01 KiB/s 173.01 KiB/s]
change:
       time:   [+0.0000% +0.0000% +0.0000] (p = NaN > 0.05)
       thrpt:  [+0.0000% +0.0000% +0.0000]
       No change in performance detected.
transfer/walltime/pacing-false/varying-seeds: Change within noise threshold.
       time:   [23.213 ms 23.251 ms 23.303 ms]
       change: [+1.1236% +1.3350% +1.5728] (p = 0.00 < 0.05)
       Change within noise threshold.
Found 3 outliers among 100 measurements (3.00%)
2 (2.00%) high mild
1 (1.00%) high severe
transfer/walltime/pacing-true/varying-seeds: Change within noise threshold.
       time:   [23.305 ms 23.326 ms 23.350 ms]
       change: [+0.4256% +0.5767% +0.7186] (p = 0.00 < 0.05)
       Change within noise threshold.
Found 1 outliers among 100 measurements (1.00%)
1 (1.00%) high severe
transfer/walltime/pacing-false/same-seed: Change within noise threshold.
       time:   [23.395 ms 23.418 ms 23.442 ms]
       change: [+0.9303% +1.0634% +1.1991] (p = 0.00 < 0.05)
       Change within noise threshold.
Found 2 outliers among 100 measurements (2.00%)
2 (2.00%) high mild
transfer/walltime/pacing-true/same-seed: Change within noise threshold.
       time:   [23.814 ms 23.843 ms 23.886 ms]
       change: [+0.7849% +0.9701% +1.1969] (p = 0.00 < 0.05)
       Change within noise threshold.
Found 3 outliers among 100 measurements (3.00%)
2 (2.00%) high mild
1 (1.00%) high severe

Download data for profiler.firefox.com or download performance comparison data.

@omansfeld
Copy link
Copy Markdown
Collaborator Author

@larseggert do you want to take another look or can I merge? The plan is to land this pref'd off in firefox with a new release next week and then run experiments on it (classic is still the default in neqo).

Copy link
Copy Markdown
Collaborator

@larseggert larseggert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Minor comments.

Comment thread neqo-transport/src/cc/classic_cc.rs
Comment thread neqo-transport/src/cc/classic_cc.rs
Comment thread neqo-transport/src/cc/hystart.rs
@larseggert larseggert added this pull request to the merge queue Mar 13, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Mar 13, 2026
@larseggert larseggert added this pull request to the merge queue Mar 13, 2026
Merged via the queue into mozilla:main with commit 6a953c2 Mar 13, 2026
204 of 206 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.

5 participants