`MessageFieldView` boxes every present nested message on decode, while owned `MessageField` defaults to inline storage
- Lingua principale
- Rust
- Stelle
- 883
- Fork
- 88
- Merge medio
- 3g 19h
- PR unite (30g)
- 42
Descrizione
`MessageFieldView` stores a set field as `Option>`, so decoding a view of a message allocates once for every nested message field that is present, however small the nested view is. The owned side made the opposite default: `MessageField` is `Inline` with recursive fields boxed automatically and `box_type_in` as the opt-out, and the `ProtoBox` docs note in passing that "view types are unaffected". So the zero-copy decode path, which is the one chosen for allocation-sensitive servers, pays a per-nested-message allocation that the owned path was redesigned to avoid.
Measured in connect-rust's small-unary gRPC benchmark (`benches/rpc`, `BenchService/Unary` with the ~300-byte `small_request()` payload: a `BenchRequest` holding a `Payload` with 15 scalar/string fields, a nested `Metadata`, two small maps, a packed `repeated int32` and a `repeated string`), with a counting global allocator on the server: the request's `decode_view` makes 7 heap allocations, and 2 of them are `MessageFieldView::set` boxing the `PayloadView` (200 B) and the `MetadataView` (72 B). The other 5 are `Vec` growth in `MapView::push` (128 B then 256 B for a 4-entry `map`, 128 B for a 2-entry one), `RepeatedView<&str>::push` (64 B) and `extend_packed_int32` (20 B). For scale, the whole connect-rust request path around that decode (HTTP/2 framing, routing, response encoding) is about 20 allocations after connectrpc/connect-rust#296, so the view decode is a third of the framework-side total for this message shape.
The box exists to break the size recursion for self-referential messages, which is the same problem `MessageField` solves with a per-field storage parameter chosen by codegen (`Inline` by default, boxed where the codegen's cycle detection or a user option asks for it). Carrying that policy over, `MessageFieldView>` with codegen emitting the boxed storage on exactly the fields where it already boxes the owned type, would make the common case allocation-free without changing what recursive schemas generate. The `DefaultViewInstance` / `Deref` behaviour is independent of where the value is stored.
The `Vec`-backed `MapView` / `RepeatedView` growth is a separate and smaller question (a first-push capacity larger than 1, or a small inline buffer, would remove most of it for short maps and lists); mentioning it here only because it is the rest of the same measurement.
Guida per i contributori
Apri la guida per i contributori
Valutazione
Questa issue non è ancora stata valutata.