Skip to content

table: use the table for messages with map fields - #477

Draft
iainmcgin wants to merge 5 commits into
table-oneoffrom
table-map
Draft

iainmcgin wants to merge 5 commits into
table-oneoffrom
table-map

Conversation

@iainmcgin

Copy link
Copy Markdown
Collaborator

Messages with map fields use the table under CodecStrategy::Table. A map field is one table entry of kind Map. Its MapVt holds the key and value kinds and two functions instantiated for the collection type (iterate, insert a decoded entry); keys and values go through the existing interpreters. A map value whose message has no table is reached through its Message impl, as in #475. Stacked on #476.

On whatsapp.proto (size text bytes, fat LTO, panic=abort, owned types only), all 752 messages use the table (749 on #476; the three left had maps). Moving them adds 6,824 bytes at z, the map interpreters. Per map field, on a synthetic schema of 600 maps over 96 key/value type pairs, table against unrolled, in text bytes: 152 vs 284 at z, 425 vs 434 at s, 536 vs 465 at 3.

Time, bare metal (c7i.metal-24xl), opt-level=3, table over unrolled at 8, 64 and 512 entries: decode 1.51x, 1.30x, 1.34x; encode 2.22x, 2.00x, 2.03x; compute_size 1.48x, 1.49x, 1.45x. One run, ±5%.

Decoding mirrors unrolled code: element-memory and recursion limits, unknown closed-enum entries kept as unknown fields, and a failed merge leaves the same state. One difference: clear() on a table message releases map capacity, where unrolled code keeps it.

The loops test for a map before dispatching. Naming the map interpreters in the dispatch arms makes opt-level=3 builds 2.6% larger on a schema without maps and 4% larger on whatsapp.proto.

buffa::table gains public MapVt, DirectMsgVt and KindMarker (and KindSlot now requires KindMarker), documented as unstable support code. A custom collection, string or bytes type in a map still falls back, with the reason "has a field with a custom string, bytes or collection type".

A map field is one table entry of kind Map whose descriptor holds the
entry's key and value kinds and two functions instantiated for the
collection type: one to iterate it, one to insert a decoded entry. The
key and value are read and written through the interpreters that
already exist, so a map adds one interpreter arm per pass and no code
per key or value type.

Decoding mirrors the unrolled codec: each entry counts against the
element-memory limit, a message value counts against the recursion limit,
an entry with an unknown closed-enum number is kept whole as an unknown
field, and a repeated key or value in one entry keeps the last.
The planner accepts a map with the default collection and default
string and bytes types, and follows the value of a map of messages as
a child. A custom collection or element type still falls back.
Every key type, every value type, entries of unusual shape, closed and
open enum values, the element-memory, unknown-field and recursion limits,
and HashMap and BTreeMap collections.
The guide, DESIGN.md, the CodecStrategy documentation and the table module
documentation listed maps among the fields that keep a message unrolled.
@github-actions

Copy link
Copy Markdown

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant