Skip to content

Consider not pre-allocating each UDP datagram in output_path #2670

Open
@mxinden

Description

@mxinden

When generating a new UDP datagram, we currently pre-allocate the maximum datagram size upfront:

// Frames for different epochs must go in different packets, but then these
// packets can go in a single datagram
let mut encoder = Encoder::with_capacity(profile.limit());

For small datagrams, this is wasteful.

How wasteful? Not too much according to our requests-per-second (i.e. RPS) benchmark and my local machine.

diff --git a/neqo-transport/src/connection/mod.rs b/neqo-transport/src/connection/mod.rs
index 929982c1..d8fb5403 100644
--- a/neqo-transport/src/connection/mod.rs
+++ b/neqo-transport/src/connection/mod.rs
@@ -2448,7 +2448,7 @@ impl Connection {
 
         // Frames for different epochs must go in different packets, but then these
         // packets can go in a single datagram
-        let mut encoder = Encoder::with_capacity(profile.limit());
+        let mut encoder = Encoder::new();
➜  neqo-bin git:(main) ✗ critcmp main no-pre-alloc
group                                               main                                   no-pre-alloc
-----                                               ----                                   ------------
1-conn/10_000-parallel-1b-resp (aka. RPS)/client    1.01    189.4±4.91ms 51.6 KElem/sec    1.00    188.0±4.12ms 51.9 KElem/sec

That said, this might not represent reality well.

Also note that it is not as easy as simply applying the above patch, given that the Encoder capacity is implicitly used, e.g. in PacketBuilder::infer_capacity:

fn infer_limit(encoder: &Encoder) -> usize {
if encoder.capacity() > 64 {
encoder.capacity()
} else {
2048
}
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions