perf: fixed-size struct fast path, integer union dispatch, zero-alloc read_next_borrowed
Targets the bench gaps from the 0.3.0 port review (commit dea96f0):
packet read was ~111-189x hand-rolled, chunk read ~18x.
- ReadPlan gains compile-time fixed_size (cached field-size sum).
Fixed structs skip the cursor size walk entirely (one bounds check
instead); fixed-size union variants skip the plan_walk_variant_size
pre-pass, eliminating the double walk of variant bytes for the
common SFTP-shaped case.
- CompositePlan::Union gains an int_keys dispatch table (pre-parsed
u64 mapping keys); byte-discriminator unions dispatch on the raw
integer instead of stringifying per read. Returned discriminator
String unchanged (public API). String-keyed fallback preserved.
- Additive SequentialReader::read_next_borrowed returns the field
name borrowed from the plan — zero allocs per field for hot loops.
read_next stays the owned-name form (single source of truth).
- plan_walk_struct_size / union shared walk: per-field format! moved
to the error path only.
- materialize: with_capacity for bytes arrays, arrays, and struct
objects.
Benches (1024 chunks/iter, criterion, pre-review baseline vs now):
- read_packet_stream: 600 -> 246 µs (~2.4x; gap to hand 189x -> ~74x)
- read_chunk_stream: 104 -> 67 µs (~1.6x; 18x -> ~11x)
- write/validate groups unchanged (within noise)
- engine_compile +8% (int_keys table + fixed-size precompute), still
one-shot
Verification: 566 tests pass, clippy -D warnings clean, wasm32 build
green. Bench baselines saved as pre-review/post-review.
This commit is contained in:
1 parent
dea96f0195
commit
d4635d28f0
4 files changed
+284
-72
No files matched your search
@@ -216,11 +216,11 @@ fn alktype_read_stream(buf: &[u8], n: usize, reader: &mut SequentialReader) -> u
|
||||
let mut total = 0usize;
|
||||
for _ in 0..n {
|
||||
reader.reset();
|
||||
let st = match reader.read_next(&buf[pos..]) {
|
||||
let st = match reader.read_next_borrowed(&buf[pos..]) {
|
||||
Ok(Some((_, FieldValue::U8(v)))) => v,
|
||||
_ => break,
|
||||
};
|
||||
let len = match reader.read_next(&buf[pos..]) {
|
||||
let len = match reader.read_next_borrowed(&buf[pos..]) {
|
||||
Ok(Some((_, FieldValue::U32(v)))) => v,
|
||||
_ => break,
|
||||
};
|
||||
@@ -372,10 +372,12 @@ fn hand_read_packet_stream(buf: &[u8], n: usize) -> usize {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Walk one variant's fields to exhaustion; returns bytes consumed.
|
||||
/// Uses `read_next_borrowed` — the zero-alloc hot-loop pattern for
|
||||
/// consumers that match or discard the field name.
|
||||
fn alktype_walk_variant(reader: &mut SequentialReader, buf: &[u8]) -> Option<usize> {
|
||||
reader.reset();
|
||||
loop {
|
||||
match reader.read_next(buf) {
|
||||
match reader.read_next_borrowed(buf) {
|
||||
Ok(Some((name, value))) => {
|
||||
black_box(name);
|
||||
black_box(&value);
|
||||
@@ -397,7 +399,7 @@ fn alktype_read_packet_stream(
|
||||
let mut total = 0usize;
|
||||
for _ in 0..n {
|
||||
packet.reset();
|
||||
let disc = match packet.read_next(&buf[pos..]) {
|
||||
let disc = match packet.read_next_borrowed(&buf[pos..]) {
|
||||
Ok(Some((_, FieldValue::Union {
|
||||
discriminator,
|
||||
variant_start,
|
||||
@@ -437,7 +439,7 @@ fn assert_packet_reader_parity(
|
||||
|
||||
let read_pkt = make_packet_bytes(5, &payload);
|
||||
packet.reset();
|
||||
match packet.read_next(&read_pkt) {
|
||||
match packet.read_next_borrowed(&read_pkt) {
|
||||
Ok(Some((_, FieldValue::Union {
|
||||
discriminator,
|
||||
variant_start,
|
||||
@@ -453,7 +455,7 @@ fn assert_packet_reader_parity(
|
||||
|
||||
let write_pkt = make_packet_bytes(6, &payload);
|
||||
packet.reset();
|
||||
match packet.read_next(&write_pkt) {
|
||||
match packet.read_next_borrowed(&write_pkt) {
|
||||
Ok(Some((_, FieldValue::Union {
|
||||
discriminator,
|
||||
variant_start,
|
||||
|
||||
Reference in new issue
Block a user