Skip to content

RUST-2401 Increase performance with facet#674

Open
abr-egn wants to merge 1 commit into
mongodb:mainfrom
abr-egn:RUST-2401/facet-perf
Open

RUST-2401 Increase performance with facet#674
abr-egn wants to merge 1 commit into
mongodb:mainfrom
abr-egn:RUST-2401/facet-perf

Conversation

@abr-egn

@abr-egn abr-egn commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

RUST-2401

This increases the performance of both the facet parsing and serialization paths via distinct sets of optimizations. There's still a significant penalty vs serde but this brings the worst case down from 5x to 3x.

For parsing, profiling showed Parser::peek to be the hot path; the main cost there was allocating a new Vec for the stack of the returned next parse state. This was an unconditional alloc that was frequently wasted, either if the stack was identical (it would be cloned and then the old value dropped) or if the parser was doing lookahead without advancing the state. To fix that, peek now returns a cheap alloc-less Delta struct that describes the change to be made to advance the state; that's only applied when necessary and even then if the stack is unchanged (i.e. iterating over fields rather than moving up/down nesting) it saves the clone+drop.

For serialization, Serializer::serialize_opaque_scalar was the hot path. That doesn't have any allocation, but it did have a linear chain of indirect function calls (value.get::<Type>()). This meant that every field walked incurred that chain, increasing by how far down that type happened to be. To avoid that, the type id is fetched once and directly compared to TypeId::of::<Type>(); because that's a const fn, those turn into compile-time constants, and then the whole set of comparisons can become an O(1) operation via a jump table or similar.

Side note: the jump table optimization was a much bigger win than the alloc removal, which surprised me quite a bit.

As far as I can tell, this was the low-hanging fruit for optimization. Getting substantially better performance would probably involve going down the same road some of the other facet format implementations do, e.g. using cranelift to generate machine code for the specific struct shapes. That would be basically a total rewrite and much more complicated at that, so I'd say we should wait and see what kind of adoption happens before investing that much time.

Benchmarks (lower is better):

Before

test tests::bench::facet_bson_deserialize ... bench:       2,186.13 ns/iter (+/- 51.62)
test tests::bench::facet_deserialize      ... bench:       1,394.19 ns/iter (+/- 29.37)
test tests::bench::facet_bson_serialize   ... bench:       1,073.20 ns/iter (+/- 47.18)
test tests::bench::facet_serialize        ... bench:         833.26 ns/iter (+/- 36.69)
test tests::bench::serde_bson_deserialize ... bench:       1,153.92 ns/iter (+/- 48.89)
test tests::bench::serde_deserialize      ... bench:         356.91 ns/iter (+/- 4.21)
test tests::bench::serde_bson_serialize   ... bench:         358.20 ns/iter (+/- 14.19)
test tests::bench::serde_serialize        ... bench:         187.91 ns/iter (+/- 1.87)

Delta vs serde:

facet_bson_deserialize:  1.9x
facet_deserialize:       3.9x
facet_bson_serialize:    3.0x
facet_serialize:         4.5x

After

test tests::bench::facet_bson_deserialize ... bench:       1,942.98 ns/iter (+/- 94.60)
test tests::bench::facet_deserialize      ... bench:       1,183.84 ns/iter (+/- 95.58)
test tests::bench::facet_bson_serialize   ... bench:         730.91 ns/iter (+/- 24.30)
test tests::bench::facet_serialize        ... bench:         537.42 ns/iter (+/- 28.59)

Delta vs serde:

facet_bson_deserialize:  1.7x
facet_deserialize:       3.3x
facet_bson_serialize:    2.0x
facet_serialize:         2.9x

Delta vs old:

facet_bson_deserialize:  0.9x
facet_deserialize:       0.8x
facet_bson_serialize:    0.7x
facet_serialize:         0.6x

@abr-egn abr-egn marked this pull request as ready for review June 30, 2026 12:32
@abr-egn abr-egn requested a review from a team as a code owner June 30, 2026 12:32
@abr-egn abr-egn requested a review from isabelatkinson June 30, 2026 12:32
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