Skip to content
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

bench(udp): run GSO, GRO and recvmmsg permutations #2010

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

mxinden
Copy link
Contributor

@mxinden mxinden commented Oct 9, 2024

  • Benchmark all permutations of GSO, GRO and recvmmsg.
  • Add support for all platforms.
  • Add test-run (i.e. cargo test --benches, single execution in debug mode) to CI.

@mxinden mxinden force-pushed the bench-throughput-gso-gro-recvmmsg branch from 1406041 to bc8825b Compare October 10, 2024 07:19
@@ -30,8 +30,13 @@ once_cell = { workspace = true }
windows-sys = { workspace = true }

[dev-dependencies]
criterion = "0.5"
criterion = { version = "0.5", default-features = false, features = ["async_tokio"] }
tokio = { workspace = true, features = ["rt", "rt-multi-thread", "net"] }
Copy link
Contributor Author

Choose a reason for hiding this comment

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

On Linux blocking recvmmsg calls will wait for all iovecs to be filled.

A blocking recvmmsg() call blocks until vlen messages have been
received or until the timeout expires. A nonblocking call reads
as many messages as are available (up to the limit specified by
vlen) and returns immediately.

https://man7.org/linux/man-pages/man2/recvmmsg.2.html

This is e.g. problematic when combined with a GSO send with less segments than iovecs in its recvmmsg counterpart.

Thus this pull request changes the benchmark to use non-blocking instead of blocking recvmmsg. To do so, it uses tokio.

See also discussion in #1993 (comment).

Copy link
Member

Choose a reason for hiding this comment

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

Suggest you keep a separate commit that makes the change from sync to async.

Comment on lines +26 to +27
#[cfg(any(target_os = "linux", target_os = "windows"))]
true,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Other platforms don't support GSO.

Needs to add apple platforms once #1993 merges.

Comment on lines +11 to +12
const MAX_IP_UDP_HEADER_SIZE: usize = 48;
const MAX_DATAGRAM_SIZE: usize = u16::MAX as usize - MAX_IP_UDP_HEADER_SIZE;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Linux allows for up to 64KiB per GSO and GRO call. That includes IP and UDP header.

Comment on lines +101 to +103
// recv.readable() can lead to false positives. Try again.
Err(e) if e.kind() == ErrorKind::WouldBlock => continue,
e => e.unwrap(),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

UdpSocket::readable is allowed to return false-positives.

The function may complete without the socket being readable. This is a false-positive and attempting a try_recv() will return with io::ErrorKind::WouldBlock.

https://docs.rs/tokio/latest/tokio/net/struct.UdpSocket.html#method.readable

Comment on lines 54 to +57
- run: cargo test
- run: cargo test --manifest-path fuzz/Cargo.toml
if: ${{ matrix.rust }} == "stable"
- run: cargo test --benches
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note that I did not simply extend the existing cargo test call, but instead added an additional cargo test --benches.

cargo test --benches executes some but not all unit tests in addition to all benchmark tests. See also rust-lang/cargo#6454.

@mxinden mxinden marked this pull request as ready for review October 10, 2024 07:34
@mxinden
Copy link
Contributor Author

mxinden commented Oct 10, 2024

@larseggert this pull request should resolve all issues raised in #1993 (review). Can you take a look?

Targeting main directly given that changes are not tied to #1993.

@mxinden
Copy link
Contributor Author

mxinden commented Oct 10, 2024

@djc @Ralith ready for a full review. Let me know if you find this pull request helpful.

Will unblock #1993.

Copy link
Member

@djc djc left a comment

Choose a reason for hiding this comment

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

I think this is probably a good change but would like to see more organization in terms of separate commits -- here's some early feedback.

@@ -30,8 +30,13 @@ once_cell = { workspace = true }
windows-sys = { workspace = true }

[dev-dependencies]
criterion = "0.5"
criterion = { version = "0.5", default-features = false, features = ["async_tokio"] }
tokio = { workspace = true, features = ["rt", "rt-multi-thread", "net"] }
Copy link
Member

Choose a reason for hiding this comment

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

Suggest you keep a separate commit that makes the change from sync to async.

use std::cmp::min;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::{io::IoSliceMut, net::UdpSocket, slice};
use quinn_udp::{RecvMeta, Transmit, UdpSocketState, BATCH_SIZE};
Copy link
Member

Choose a reason for hiding this comment

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

Nit: would be good to do StdExternalCrate import blocks here.

use tokio::io::Interest;
use tokio::runtime::Runtime;

const MAX_IP_UDP_HEADER_SIZE: usize = 48;
Copy link
Member

Choose a reason for hiding this comment

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

Nit: constants should go near the bottom.

io::{ErrorKind, IoSliceMut},
net::{Ipv4Addr, Ipv6Addr, UdpSocket},
};
use tokio::io::Interest;
Copy link
Member

Choose a reason for hiding this comment

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

Nit: for consistency, there should be one import statement for all tokio imports.

}
})
});
}
}

fn new_socket(rt: &mut Runtime) -> (tokio::net::UdpSocket, UdpSocketState) {
Copy link
Member

Choose a reason for hiding this comment

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

Please keep the extraction of this function in a separate commit.

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