Performance
Binary:Attrs backed by SmallVec for inline storage (#819)
Attrs previously used a plain Vec as its backing store, so every node on the encode path paid one heap allocation just for the attribute buffer. In a steady-state send/receive profile this was ~7 allocations per message stanza. On a group fanout it was two per recipient (to + enc).
Attrs now uses AttrsVec — SmallVec<[(Cow<'static, str>, NodeValue); 2]> — so nodes carrying ≤2 attributes keep them on the stack alongside the node with zero heap allocation. Nodes with ≥3 attributes spill to the heap as before.
Measured impact (iai-callgrind, instruction counts):
| Benchmark | Delta |
|---|---|
marshal_allocating (typical stanza) | −6.6% instructions |
marshal_reusing_buffer | −6.5% instructions |
marshal_many_children_allocating (2048 children) | −9.9% instructions, −10% estimated cycles, −25% RAM hits |
marshal_long_string | +4.4% (+208 instructions, content-dominated shape) |
| unmarshal / unpack / attr_parser | unchanged |
Breaking changes
Attrs.0 type (wacore-binary)
Attrs.0 changes from Vec<(Cow<'static, str>, NodeValue)> to AttrsVec. Construction via Attrs::new(), Attrs::with_capacity(), NodeBuilder, iteration, and the serde representation are all unchanged (the serde wire format is byte-identical). Code that held a reference to Attrs.0 typed as Vec needs updating:
AttrsVec is re-exported as wacore_binary::node::AttrsVec. The spilled() method on SmallVec reports whether the list has overflowed to the heap.
IqError::Disconnected now carries Box<Node>
Both wacore::request::IqError and the main crate’s mirror change Disconnected(Node) to Disconnected(Box<Node>). The larger Node size after the SmallVec inline array triggered clippy’s large_enum_variant; boxing the rare disconnect payload is correct regardless.