#serde #flatten #serialization

serde_fast_flatten

A faster flatten implementation for serde

4 releases

new 0.1.3 Feb 16, 2025
0.1.2 Feb 15, 2025
0.1.1 Feb 15, 2025
0.1.0 Feb 11, 2025

#320 in Encoding

Download history 393/week @ 2025-02-11

393 downloads per month
Used in serde_fast_flatten_derive

MIT/Apache

34KB
718 lines

Serde fast flatten

This crate provides a drop-in replacement for serde auto-derive macros with a faster flatten implementation that, incidentally, is also compatible with non-self-describing formats.

Summary

The Deserialize implementation generated by serde's auto-derive macro for flattened struct fields, is quite slow compared to the unflattened version, because it needs to store the unrecognized fields in the parent structure for subsequent deserilization by the child structure.

This crate provides traits that allow a more performant implementation of struct flattening, along with auto-derive macros to help implementing them. It also includes some tests and a benchmark (see below).

Usage

In Cargo.toml, add the following dependency:

serde_fast_flatten = { version = "0.1.3", features = ["derive"] }

In your code, replace the auto-derive macros serde::Serialize and serde::Deserialize with serde::SerializeFields and serde::DeserializeFields, respectively. This is needed for all structs containing a #[serde(flatten)] annotation and all structs referenced by such annotation.

// use serde::{Serialize, Deserialize};
use serde_fast_flatten::{SerializeFields, DeserializeFields};

// #[derive(Serialize, Deserialize)]
#[derive(SerializeFields, DeserializeFields)]
struct Paged<T> {
    #[serde(flatten)]
    params: PageParams,
    items: Vec<T>,
}

// #[derive(Serialize, Deserialize)]
#[derive(SerializeFields, DeserializeFields)]
struct PageParams {
    page: u32,
    limit: u32,
}

That's it! The auto-derive macros will generate implementations for SerializeFields and DeserializeFields, as well as Serialize and Deserialize implementations based on them.

Compatibility

The test suite currently includes roundtrip tests for serde_json, serde_yaml, serde_cbor (normal and packed repr), bincode and bitcode.

For serde_json and serde_yaml, the serialized output is the same as the standard serde implementation. Using serde_cbor, the output differs. bincode and bitcode both fail on serde's auto-derived flatten implementation, and work correctly using the serde_fast_flatten-based implementation.

The auto-derive macros currently only work on structs, not on enums, and are only tested with #[serde(flatten)] attributes.

Benchmarks

A simple benchmark can be run using cargo bench. It serializes and deserializes a simple structure with serde_json. On my machine (Intel Ultra 9 185H) this gives the following results:

Name Low Median High
serialize/serde_flatten 53.415 µs 53.522 µs 53.634 µs
serialize/serde_unflattened 76.089 µs 76.279 µs 76.483 µs
serialize/fast_flatten 54.318 µs 54.467 µs 54.647 µs
deserialize/serde_flatten 279.46 µs 279.89 µs 280.32 µs
deserialize/serde_unflattened 122.75 µs 123.06 µs 123.43 µs
deserialize/fast_flatten 124.03 µs 124.20 µs 124.38 µs

When serializing, there is little difference between the implementation auto-derived by serde and the one written using serde_fast_flatten. The unflattened representation serializes slower, probably due to the increased output size.

For deserialization, however, there is a big difference between the performance of serde's implementation and the one by serde_fast_flatten. While serde's implementation takes about twice as long to deserialize the test values from the flattened representation as deserializing from an unflattened representation, serde_fast_flatten's performance approaches that of the unflattened representation.

Roadmap

  • Add support for internally-tagged and adjacently-tagged enums (with fast path if the tag is the first field encountered, and fallback to content deserializer otherwise)
  • Add support for all serde attributes
  • Add more example types and formats to test suite

Stability

This project is in alpha stage. Even though there are currently no guarantees, the intention is to progressively enhance the crate for its primary goal of providing drop-in replacements for the serde auto-derive macros.

This means that if the auto-derive macros provided by this cratecurrently work for your structures (i.e. the code compiles and the Serialize and Deserialize implementations generated by SerializeFields and DeserializeFields work as expected), we intend to keep them working in the future. The best way to ensure stability for your use-case is to contribute tests.

The traits added by this crate (SerializeFields, DeserializeFields, FieldDeserializer) are subject to change in function of features to be added to the auto-derive macros.

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Dependencies

~0.3–1MB
~21K SLoC