Skip to content

fix(proto): count udp_rx.ios per udp datagram instead of quic packet#725

Draft
divagant-martian wants to merge 4 commits into
mainfrom
overcounted-udp-rx-ios
Draft

fix(proto): count udp_rx.ios per udp datagram instead of quic packet#725
divagant-martian wants to merge 4 commits into
mainfrom
overcounted-udp-rx-ios

Conversation

@divagant-martian

Copy link
Copy Markdown
Collaborator

Description

Fixes counting of rx io operations. Adds a regression test with the broken invariant

Breaking Changes

n/a

Notes & open questions

n/a

Change checklist

  • Self-review.
  • Tests if relevant.

@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown

Performance Comparison Report

22beec5b8747d02722ad95e33d7211685c91a114 - artifacts

No results available

---
f695e4394a12c0ece2b6f7a208e6793593c72e73 - artifacts

Raw Benchmarks (localhost)

Scenario noq upstream Delta CPU (avg/max)
large-single 5256.0 Mbps 7947.4 Mbps -33.9% 97.4% / 98.7%
medium-concurrent 5538.6 Mbps 7774.8 Mbps -28.8% 96.1% / 97.7%
medium-single 4032.5 Mbps 4470.4 Mbps -9.8% 96.5% / 98.4%
small-concurrent 3889.9 Mbps 5135.4 Mbps -24.3% 98.0% / 100.0%
small-single 3497.6 Mbps 4584.4 Mbps -23.7% 95.4% / 97.8%

Netsim Benchmarks (network simulation)

Condition noq upstream Delta
ideal 3086.4 Mbps 4022.9 Mbps -23.3%
lan 782.4 Mbps 803.8 Mbps -2.7%
lossy 55.9 Mbps 69.8 Mbps -20.0%
wan 83.8 Mbps 83.8 Mbps ~0%

Summary

noq is 24.8% slower on average

---
5f02c8afa0cb1989d58eb53083299a22356488d0 - artifacts

Raw Benchmarks (localhost)

Scenario noq upstream Delta CPU (avg/max)
large-single 5878.0 Mbps 7878.1 Mbps -25.4% 97.5% / 98.8%
medium-concurrent 5493.0 Mbps 7788.0 Mbps -29.5% 96.5% / 98.0%
medium-single 3942.6 Mbps 4730.2 Mbps -16.7% 96.7% / 98.6%
small-concurrent 3842.6 Mbps 5142.5 Mbps -25.3% 97.6% / 99.7%
small-single 3572.0 Mbps 4666.6 Mbps -23.5% 96.1% / 98.4%

Netsim Benchmarks (network simulation)

Condition noq upstream Delta
ideal 3044.8 Mbps 4078.5 Mbps -25.3%
lan 782.4 Mbps 810.4 Mbps -3.5%
lossy 69.8 Mbps 59.1 Mbps +18.1%
wan 83.8 Mbps 83.8 Mbps ~0%

Summary

noq is 24.2% slower on average

@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown

Documentation for this PR has been generated and is available at: https://n0-computer.github.io/noq/pr/725/docs/noq/

Last updated: 2026-06-25T21:06:34Z

@n0bot n0bot Bot added this to iroh Jun 25, 2026
@github-project-automation github-project-automation Bot moved this to 🚑 Needs Triage in iroh Jun 25, 2026

@matheus23 matheus23 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would be neat to take our learnings about the nuance of stats to the doc comments of said stats.

E.g. we could add the invariants we expect (ios <= datagrams) to the docstring, and the fact that discarded datagrams are not counted towards ios or datagrams.

let rx = &mut self.path_stats.for_path(path_id).udp_rx;
rx.datagrams += 1;
rx.bytes += first_decode.len() as u64;
rx.ios += 1;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It's weird that rx.ios == rx.datagrams in this case.

For one, shouldn't ios count a full GRO batch as one operation, but datagrams should count each one in the batch?

Also, I think even if we early_discard_packet above, we should count it in ios, but not in datagrams? Ah shit, ignore me we need to know the Path ID. This is better anyways, because then bytes and datagrams can be better in relation to each other.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is rather difficult on the receive side. You'd have to do it in the EndpointDriver I guess but you don't have enough information there let alone access to the stats. I guess you could do something convoluted like keep the counter there per CID and than send the counter over with each datagram delivered and than keep per CID stats over here that can be summed to return the right per-path and per-connection values at query time.

But that's pretty convoluted. And looking at RecvState::poll_socket I'm not even sure it'll be that simple to achieve. I think this might not be worth it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yeah totally fair. So this will mean that currently ios will always be equal to datagrams? I'm fine with that. Maybe at some point we'll be refactoring things so it can become a useful stat.

Comment on lines +2204 to +2206
#[test]
fn udp_rx_ios_overcounts_batched_udp_datagrams() {
// this is a regression test

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I like to just put "regression_" into the test name itself. The test name also seems to assert that ios should overcount datagrams, which is backwards (I personally interpret test names like that, and I thought that's somewhat common).

Suggested change
#[test]
fn udp_rx_ios_overcounts_batched_udp_datagrams() {
// this is a regression test
#[test]
fn regression_udp_rx_ios_lt_datagrams() {
// this is a regression test

let rx = &mut self.path_stats.for_path(path_id).udp_rx;
rx.datagrams += 1;
rx.bytes += first_decode.len() as u64;
rx.ios += 1;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is rather difficult on the receive side. You'd have to do it in the EndpointDriver I guess but you don't have enough information there let alone access to the stats. I guess you could do something convoluted like keep the counter there per CID and than send the counter over with each datagram delivered and than keep per CID stats over here that can be summed to return the right per-path and per-connection values at query time.

But that's pretty convoluted. And looking at RecvState::poll_socket I'm not even sure it'll be that simple to achieve. I think this might not be worth it.

@divagant-martian

Copy link
Copy Markdown
Collaborator Author

The more I check these UdpStats the less sense they make.

@divagant-martian divagant-martian marked this pull request as draft June 27, 2026 14:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: 🚑 Needs Triage

Development

Successfully merging this pull request may close these issues.

3 participants