Skip to content

feat: More ECN and DSCP stats #2505

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Apr 4, 2025
Merged

Conversation

larseggert
Copy link
Collaborator

@larseggert larseggert commented Mar 18, 2025

Inspired by @martinduke's IETF presentation on ECN, add some more stats around what packet types we send and receive with different ECN codepoints.

This will require additional glue code to ship these stats back as telemetry.

Debug output:

  ecn:
    tx:
      Initial Count({NotEct: 0, Ect1: 0, Ect0: 2, Ce: 0})
      Handshake Count({NotEct: 0, Ect1: 0, Ect0: 4, Ce: 0})
      Short Count({NotEct: 0, Ect1: 0, Ect0: 15, Ce: 0})
    acked:
      Initial Count({NotEct: 0, Ect1: 0, Ect0: 2, Ce: 0})
      Handshake Count({NotEct: 0, Ect1: 0, Ect0: 4, Ce: 0})
      Short Count({NotEct: 0, Ect1: 0, Ect0: 6, Ce: 0})
    rx:
      Initial Count({NotEct: 0, Ect1: 0, Ect0: 2, Ce: 0})
      Handshake Count({NotEct: 0, Ect1: 0, Ect0: 3, Ce: 0})
      Short Count({NotEct: 5, Ect1: 0, Ect0: 17, Ce: 0})
    path validation outcomes: ValidationCount({Capable: 1, NotCapable(BlackHole): 0, NotCapable(Bleaching): 0, NotCapable(ReceivedUnsentECT1): 0})
    mark transitions:
      First NotEct to Ect0 (Short, 11)
      First Ect0 to NotEct (Short, 6)
  dscp: Cs0: 27

Inspired by @martinduke's [IETF presentation on
ECN](https://datatracker.ietf.org/meeting/122/materials/slides-122-tsvwg-udp-ecn-00),
add some more stats around what packet types we send and receive with different
ECN codepoints.

This will require additional glue code to ship these stats back as telemetry.
Copy link

github-actions bot commented Mar 18, 2025

Failed Interop Tests

QUIC Interop Runner, client vs. server, differences relative to 182afed.

neqo-latest as client

neqo-latest as server

All results

Succeeded Interop Tests

QUIC Interop Runner, client vs. server

neqo-latest as client

neqo-latest as server

Unsupported Interop Tests

QUIC Interop Runner, client vs. server

neqo-latest as client

neqo-latest as server

Copy link

github-actions bot commented Mar 18, 2025

Benchmark results

Performance differences relative to 054b7cb.

1-conn/1-100mb-resp/mtu-1504 (aka. Download)/client: 💔 Performance has regressed.
       time:   [735.04 ms 739.68 ms 744.37 ms]
       thrpt:  [134.34 MiB/s 135.19 MiB/s 136.05 MiB/s]
change:
       time:   [+1.8860% +2.7051% +3.5675%] (p = 0.00 < 0.05)
       thrpt:  [-3.4446% -2.6338% -1.8511%]

Found 1 outliers among 100 measurements (1.00%)
1 (1.00%) high mild

1-conn/10_000-parallel-1b-resp/mtu-1504 (aka. RPS)/client: No change in performance detected.
       time:   [348.30 ms 349.82 ms 351.32 ms]
       thrpt:  [28.464 Kelem/s 28.586 Kelem/s 28.711 Kelem/s]
change:
       time:   [-0.6378% +0.0565% +0.7257%] (p = 0.87 > 0.05)
       thrpt:  [-0.7205% -0.0564% +0.6419%]
1-conn/1-1b-resp/mtu-1504 (aka. HPS)/client: No change in performance detected.
       time:   [25.026 ms 25.187 ms 25.354 ms]
       thrpt:  [39.442  elem/s 39.703  elem/s 39.959  elem/s]
change:
       time:   [-0.1797% +0.6585% +1.5540%] (p = 0.14 > 0.05)
       thrpt:  [-1.5302% -0.6542% +0.1800%]
1-conn/1-100mb-req/mtu-1504 (aka. Upload)/client: Change within noise threshold.
       time:   [1.8275 s 1.8447 s 1.8627 s]
       thrpt:  [53.684 MiB/s 54.210 MiB/s 54.718 MiB/s]
change:
       time:   [-3.3122% -1.7298% -0.1833%] (p = 0.04 < 0.05)
       thrpt:  [+0.1837% +1.7603% +3.4257%]

Found 4 outliers among 100 measurements (4.00%)
4 (4.00%) high mild

decode 4096 bytes, mask ff: No change in performance detected.
       time:   [12.086 µs 12.127 µs 12.174 µs]
       change: [-1.1286% -0.3225% +0.3639%] (p = 0.43 > 0.05)

Found 17 outliers among 100 measurements (17.00%)
2 (2.00%) low severe
3 (3.00%) low mild
3 (3.00%) high mild
9 (9.00%) high severe

decode 1048576 bytes, mask ff: No change in performance detected.
       time:   [3.0769 ms 3.0862 ms 3.0973 ms]
       change: [-0.6410% -0.1483% +0.3284%] (p = 0.55 > 0.05)

Found 9 outliers among 100 measurements (9.00%)
1 (1.00%) high mild
8 (8.00%) high severe

decode 4096 bytes, mask 7f: No change in performance detected.
       time:   [20.139 µs 20.187 µs 20.242 µs]
       change: [-0.3563% -0.0774% +0.2078%] (p = 0.60 > 0.05)

Found 14 outliers among 100 measurements (14.00%)
2 (2.00%) low severe
1 (1.00%) low mild
11 (11.00%) high severe

decode 1048576 bytes, mask 7f: No change in performance detected.
       time:   [5.2526 ms 5.2641 ms 5.2773 ms]
       change: [-0.3490% -0.0271% +0.3258%] (p = 0.89 > 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:   [7.0100 µs 7.0319 µs 7.0603 µs]
       change: [-0.5338% -0.0053% +0.4403%] (p = 0.99 > 0.05)

Found 17 outliers among 100 measurements (17.00%)
4 (4.00%) low severe
3 (3.00%) low mild
3 (3.00%) high mild
7 (7.00%) high severe

decode 1048576 bytes, mask 3f: No change in performance detected.
       time:   [1.7922 ms 1.7979 ms 1.8049 ms]
       change: [-0.3569% +0.1104% +0.5890%] (p = 0.64 > 0.05)

Found 6 outliers among 100 measurements (6.00%)
6 (6.00%) high severe

1 streams of 1 bytes/multistream: 💔 Performance has regressed.
       time:   [73.501 µs 74.577 µs 76.108 µs]
       change: [+1.5679% +3.1057% +5.2299%] (p = 0.00 < 0.05)

Found 2 outliers among 100 measurements (2.00%)
2 (2.00%) high severe

1000 streams of 1 bytes/multistream: 💔 Performance has regressed.
       time:   [25.339 ms 25.378 ms 25.418 ms]
       change: [+1.5118% +1.7329% +1.9432%] (p = 0.00 < 0.05)
10000 streams of 1 bytes/multistream: 💔 Performance has regressed.
       time:   [1.7058 s 1.7074 s 1.7090 s]
       change: [+1.2315% +1.3628% +1.4929%] (p = 0.00 < 0.05)

Found 19 outliers among 100 measurements (19.00%)
3 (3.00%) low severe
8 (8.00%) low mild
2 (2.00%) high mild
6 (6.00%) high severe

1 streams of 1000 bytes/multistream: Change within noise threshold.
       time:   [75.461 µs 76.859 µs 78.683 µs]
       change: [+0.6846% +3.1506% +5.8967%] (p = 0.00 < 0.05)

Found 3 outliers among 100 measurements (3.00%)
3 (3.00%) high severe

100 streams of 1000 bytes/multistream: Change within noise threshold.
       time:   [3.4201 ms 3.4269 ms 3.4341 ms]
       change: [+0.6923% +0.9885% +1.2704%] (p = 0.00 < 0.05)

Found 24 outliers among 100 measurements (24.00%)
24 (24.00%) high severe

1000 streams of 1000 bytes/multistream: Change within noise threshold.
       time:   [143.86 ms 143.94 ms 144.01 ms]
       change: [+0.5644% +0.6399% +0.7129%] (p = 0.00 < 0.05)

Found 2 outliers among 100 measurements (2.00%)
2 (2.00%) high mild

coalesce_acked_from_zero 1+1 entries: No change in performance detected.
       time:   [94.655 ns 95.015 ns 95.375 ns]
       change: [-0.4691% +0.0932% +0.6824%] (p = 0.77 > 0.05)

Found 12 outliers among 100 measurements (12.00%)
9 (9.00%) high mild
3 (3.00%) high severe

coalesce_acked_from_zero 3+1 entries: No change in performance detected.
       time:   [112.71 ns 113.08 ns 113.47 ns]
       change: [-0.1562% +0.2874% +0.8083%] (p = 0.22 > 0.05)

Found 14 outliers among 100 measurements (14.00%)
14 (14.00%) high severe

coalesce_acked_from_zero 10+1 entries: No change in performance detected.
       time:   [112.38 ns 112.94 ns 113.60 ns]
       change: [-0.2043% +0.4505% +1.1414%] (p = 0.20 > 0.05)

Found 16 outliers among 100 measurements (16.00%)
3 (3.00%) low severe
13 (13.00%) high severe

coalesce_acked_from_zero 1000+1 entries: Change within noise threshold.
       time:   [94.130 ns 94.494 ns 94.882 ns]
       change: [+0.7154% +1.8366% +3.1210%] (p = 0.00 < 0.05)

Found 13 outliers among 100 measurements (13.00%)
8 (8.00%) high mild
5 (5.00%) high severe

RxStreamOrderer::inbound_frame(): Change within noise threshold.
       time:   [115.55 ms 115.60 ms 115.67 ms]
       change: [+0.4598% +0.5367% +0.6153%] (p = 0.00 < 0.05)

Found 19 outliers among 100 measurements (19.00%)
5 (5.00%) low severe
1 (1.00%) low mild
3 (3.00%) high mild
10 (10.00%) high severe

SentPackets::take_ranges: No change in performance detected.
       time:   [8.3562 µs 8.6462 µs 8.9254 µs]
       change: [-0.5633% +2.9601% +6.4513%] (p = 0.10 > 0.05)

Found 26 outliers among 100 measurements (26.00%)
2 (2.00%) low severe
17 (17.00%) low mild
6 (6.00%) high mild
1 (1.00%) high severe

transfer/pacing-false/varying-seeds: 💚 Performance has improved.
       time:   [35.299 ms 35.364 ms 35.429 ms]
       change: [-3.7113% -3.4502% -3.2045%] (p = 0.00 < 0.05)
transfer/pacing-true/varying-seeds: 💚 Performance has improved.
       time:   [36.183 ms 36.277 ms 36.371 ms]
       change: [-4.2521% -3.8515% -3.4731%] (p = 0.00 < 0.05)

Found 2 outliers among 100 measurements (2.00%)
2 (2.00%) high mild

transfer/pacing-false/same-seed: 💚 Performance has improved.
       time:   [35.344 ms 35.390 ms 35.439 ms]
       change: [-3.4056% -3.2326% -3.0433%] (p = 0.00 < 0.05)

Found 2 outliers among 100 measurements (2.00%)
2 (2.00%) high mild

transfer/pacing-true/same-seed: 💚 Performance has improved.
       time:   [37.052 ms 37.121 ms 37.192 ms]
       change: [-4.7178% -4.4772% -4.2185%] (p = 0.00 < 0.05)

Found 2 outliers among 100 measurements (2.00%)
2 (2.00%) high mild

Client/server transfer results

Performance differences relative to 054b7cb.

Transfer of 33554432 bytes over loopback, 30 runs. All unit-less numbers are in milliseconds.

Client Server CC Pacing Mean ± σ Min Max MiB/s ± σ Δ main Δ main
neqo neqo reno on 354.5 ± 95.1 293.8 748.2 90.3 ± 0.3 27.6 8.4%
neqo neqo reno 383.8 ± 155.3 293.8 844.4 83.4 ± 0.2 7.3 1.9%
neqo neqo cubic on 337.9 ± 54.7 300.9 513.3 94.7 ± 0.6 -24.9 -6.9%
neqo neqo cubic 326.2 ± 35.5 302.6 501.7 98.1 ± 0.9 -2.5 -0.8%
google neqo reno on 757.3 ± 81.1 575.2 874.2 42.3 ± 0.4 -8.5 -1.1%
google neqo reno 758.0 ± 92.9 562.3 989.0 42.2 ± 0.3 -11.4 -1.5%
google neqo cubic on 763.2 ± 94.1 567.6 1028.1 41.9 ± 0.3 -6.8 -0.9%
google neqo cubic 765.0 ± 93.2 564.9 941.9 41.8 ± 0.3 -7.8 -1.0%
google google 577.4 ± 25.5 554.7 679.3 55.4 ± 1.3 -9.8 -1.7%
neqo msquic reno on 268.2 ± 35.3 245.7 440.7 119.3 ± 0.9 -3.0 -1.1%
neqo msquic reno 270.6 ± 34.8 244.4 427.3 118.3 ± 0.9 -1.5 -0.6%
neqo msquic cubic on 267.6 ± 31.9 244.5 416.7 119.6 ± 1.0 -11.7 -4.2%
neqo msquic cubic 270.1 ± 33.8 236.6 413.7 118.5 ± 0.9 5.7 2.2%
msquic msquic 191.6 ± 45.5 147.5 319.4 167.0 ± 0.7 3.9 2.1%

⬇️ Download logs

@martinduke
Copy link

Inspired by @martinduke's IETF presentation on ECN, add some more stats around what packet types we send and receive with different ECN codepoints.

This will require additional glue code to ship these stats back as telemetry.

This is the correct presentation: link.

The key insight, IMO, is what ECT codepoints appear in the same connection. IIUC this will provide that, but in even more detail, by counting the number of packets in each.

Another interesting metric is "how many packets go by before the first codepoint transition," which is an indicator whether this is just a result of handshake packets being Not-ECT or something else.

@larseggert
Copy link
Collaborator Author

This is the correct presentation: link.

Thanks, corrected above.

Another interesting metric is "how many packets go by before the first codepoint transition," which is an indicator whether this is just a result of handshake packets being Not-ECT or something else.

"First codepoint transition" as in "away from NotEct" or as in "away from whatever codepoint was used on the first packet"?

@martinduke
Copy link

This is the correct presentation: link.

Thanks, corrected above.

Another interesting metric is "how many packets go by before the first codepoint transition," which is an indicator whether this is just a result of handshake packets being Not-ECT or something else.

"First codepoint transition" as in "away from NotEct" or as in "away from whatever codepoint was used on the first packet"?

What I did was the latter, but noting the packet count at each transition type would of course be the richest data set. NotEct to something else is generally a handshake->short header transition, ECT->Not is a validation failure, and ECT->CE probably tells you something about the path and your CCA, though I'm not sure what. It's just a suggestion; do it if you think it's valuable.

@larseggert
Copy link
Collaborator Author

...but noting the packet count at each transition type would of course be the richest data set. NotEct to something else is generally a handshake->short header transition, ECT->Not is a validation failure, and ECT->CE probably tells you something about the path and your CCA, though I'm not sure what. It's just a suggestion; do it if you think it's valuable.

Need to think about this, as our telemetry system (https://github.com/mozilla/glean/) makes it purposefully a bit challenging to gather variable-length data.

Copy link
Collaborator

@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.

Data exposure looks reasonable. That said, not sure how to gather the data with Glean.

I will watch Martin's talk recording to understand the greater picture.

Can we hold off here until then?

@larseggert
Copy link
Collaborator Author

Sure. This is by no means urgent, and maybe we want to discuss a more principled approach to adding QUIC telemetry first.

@larseggert larseggert changed the title feat: More ECN stats feat: More ECN and DSCP stats Mar 20, 2025
Copy link
Collaborator

@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.

Watched Martin's talk now. Thank you for sharing the data.

I don't know which concrete engineering decisions we would support with the data exposed here. Do you have something concrete in mind, Lars?

@larseggert larseggert requested a review from mxinden April 2, 2025 14:55
Co-authored-by: Max Inden <[email protected]>
Signed-off-by: Lars Eggert <[email protected]>
@@ -33,6 +41,7 @@ fn unknown_version() {
unknown_version_packet.resize(MIN_INITIAL_PACKET_SIZE, 0x0);
drop(client.process(Some(datagram(unknown_version_packet)), now()));
assert_eq!(1, client.stats().dropped_rx);
assert_dscp(&client.stats());
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: I generally strive for a single unit test to test a single property only.

Rational: When a unit test tests multiple properties, a test failure becomes difficult to interpret, i.e. one cannot immediately tell which property is genuinely broken or whether adjusting the test would inadvertently discard another important check. By keeping each test focused on a single property, one ensures that test failures clearly indicate which behavior needs attention, making it safer and more straightforward to update or refactor ones code.

With that in mind, I am not a fan of adding assert_dscp to only-slightly-related tests, but instead I would prefer separate dedicated tests.

Feel free to ignore this. I don't feel strongly about it. It might not even be worth the effort here.

@larseggert larseggert enabled auto-merge April 3, 2025 18:25
@larseggert larseggert disabled auto-merge April 4, 2025 06:05
@larseggert larseggert merged commit 41036fc into mozilla:main Apr 4, 2025
79 of 81 checks passed
@larseggert larseggert deleted the feat-more-ecn-stats branch April 7, 2025 10:13
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.

3 participants