Skip to content

A multi-frame burst still allocates its result storage #1138

Description

@jlucaso1

The out-parameter added in #1137 removes the per-burst allocation only for the single-frame path, which is the common one and is where the measured -1.43 allocations per message came from. A burst of 2-4 frames still allocates.

Where it goes

Client::send_raw_bytes_burst (src/client/messaging.rs) drives the multi-frame path through futures::future::join_all, which allocates its own storage for the futures and returns a fresh Vec of results. That Vec is then copied into the caller's buffer, so the path pays what it paid before the out-parameter existed, plus one traversal.

Reported by Codex on #1137 and correct: the optimisation does not reach this path.

Why it was not fixed there

encrypt_and_send sends a SendJob down a channel and awaits a oneshot, so the concurrency is what gets every frame of a burst to the sender before any of them completes. That is what lets the Noise layer coalesce them into one transport write (#1119, #1121). Awaiting the futures in sequence would remove the allocation and the batching with it.

What a fix would look like

Split encrypt_and_send into an enqueue half returning the oneshot::Receiver and an await half. The burst then enqueues every job first, holds the receivers in a SmallVec<[_; 4]> (both call sites cap the burst at 4, so that is inline), and awaits them in order. No join_all, no result Vec, ordering preserved because the receivers are awaited in the order the frames were drained.

That is a change to the send path rather than to the burst helper, which is why it was kept out of a PR about allocation counts on the DM round trip.

What to watch when doing it

  • A frame whose enqueue fails must still leave the caller's results aligned with the frames that were drained, or a failure gets reported against the wrong frame.
  • frames must drain on every exit, including the error path. raw_bytes_burst_drains_input_when_disconnected pins this today.
  • The single-frame fast path must keep filling the caller's buffer rather than replacing it; raw_bytes_burst_drains_and_reuses_input_on_happy_paths compares as_ptr() across both calls.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions