Skip to content

High-Throughput Numeric Bodies

When a whole REPE message body is a contiguous numeric slice -- &[f64], &[i32], a &[Complex<f64>], a matrix of samples -- repe can encode and decode it as a BEVE typed array in a single bulk copy, bypassing serde's element-by-element walk. On little-endian targets the encode, decode, and framing are O(1) in the element count (a header plus one memcpy), versus the per-element traversal a serde body pays.

This is opt-in and applies to a whole-body numeric slice. A numeric Vec<T> nested as a field inside a larger struct still goes through serde (serde never exposes the field's backing slice), so reach for it when the entire body is the array.

Encoding

MessageBuilder::body_typed_slice and body_complex_slice encode a slice in one bulk write and set BodyFormat::Beve:

use repe::{Complex, Message};

let samples: Vec<f64> = (0..4096).map(|i| (i as f64 * 0.25).sin()).collect();
let msg = Message::builder()
    .query_str("/spectra/ingest")
    .body_typed_slice(&samples)
    .build();

let spectrum: Vec<Complex<f64>> = /* ... */;
let cmsg = Message::builder()
    .body_complex_slice(&spectrum)
    .build();

The bytes are identical to body_beve(&samples) (the serde path), so a bulk sender and a serde receiver -- or the reverse -- interoperate freely. The difference is only the cost of producing them.

Decoding

Message::decode_typed_slice / decode_complex_slice read the body back in one bounds-checked bulk copy:

let back: Vec<f64> = msg.decode_typed_slice()?;
let cback: Vec<Complex<f64>> = cmsg.decode_complex_slice()?;

They error with RepeError::UnexpectedBodyFormat if the body is not BodyFormat::Beve, and RepeError::Beve if the bytes are not a typed array of the requested element type (wrong class/width, or a truncated payload) -- never a silent reinterpretation. Because the bytes match the serde encoding, decode_typed_slice also reads a body that was produced by body_beve(&Vec<T>), and conversely beve_body::<Vec<T>>() reads a body_typed_slice body.

Streaming a large body with no body buffer

For a body too large to hold a second copy of, write_message_typed_slice sizes the body in closed form (beve::typed_slice_size, no traversal) and writes the payload straight to the sink:

use repe::{Header, write_message_typed_slice};

let big: Vec<f64> = /* millions of samples */;
write_message_typed_slice(&mut sink, Header::new(), b"/spectra/stream", &big)?;

The wire frame is byte-for-byte identical to building a body_typed_slice message and writing it with write_message; it just never materializes the body or a wire-frame Vec. (There is no streaming writer for complex bodies yet -- build a Message with body_complex_slice and frame it with write_message.)

Performance

Framing a whole-body numeric Vec<f64>, serde streaming (serialized_size + to_writer_streaming, two O(payload) element walks) vs the typed-slice fast path (O(1) size + one bulk write), from benches/wire_serialization.rs:

elements serde stream typed slice speedup
64 268 ns 19 ns ~14x
4 096 17.6 us 535 ns ~33x
65 536 281 us 8.9 us ~31x
1 048 576 4.44 ms 168 us ~26x

The win grows with body size until it is bound by memory bandwidth (the bulk copy), where it plateaus around 25-33x.

See examples/typed_numeric_body.rs for a runnable end-to-end demo.