When profiling toml_edit, a hand-written Deserialize for an untagged enum took a benchmark for a common case in cargo from 484us to 181us (for a single Cargo.toml file, using this in the dependency resolving would multiple that by hundreds).
Root Cause
The derive's untagged enum implementation tries each variant, one after another.
#[automatically_derived]
impl <'de> _serde::Deserialize<'de> for Value {
fn deserialize<__D>(__deserializer: __D)
-> _serde::__private::Result<Self, __D::Error> where
__D: _serde::Deserializer<'de> {
let __content =
match <_serde::__private::de::Content as
_serde::Deserialize>::deserialize(__deserializer)
{
_serde::__private::Ok(__val) => __val,
_serde::__private::Err(__err) => {
return _serde::__private::Err(__err);
}
};
if let _serde::__private::Ok(__ok) =
_serde::__private::Result::map(<i64 as
_serde::Deserialize>::deserialize(_serde::__private::de::ContentRefDeserializer::<__D::Error>::new(&__content)),
Value::Integer)
{
return _serde::__private::Ok(__ok);
}
if let _serde::__private::Ok(__ok) =
_serde::__private::Result::map(<f64 as
_serde::Deserialize>::deserialize(_serde::__private::de::ContentRefDeserializer::<__D::Error>::new(&__content)),
Value::Float) {
return _serde::__private::Ok(__ok);
}
// ...
if let _serde::__private::Ok(__ok) =
_serde::__private::Result::map(<Table as
_serde::Deserialize>::deserialize(_serde::__private::de::ContentRefDeserializer::<__D::Error>::new(&__content)),
Value::Table) {
return _serde::__private::Ok(__ok);
}
_serde::__private::Err(_serde::de::Error::custom("data did not match any variant of untagged enum Value"))
}
}
For each failure, we generate an error which allocates its message and is adverised as a #[cold]
#[cold]
fn invalid_type(unexp: Unexpected, exp: &Expected) -> Self {
Error::custom(format_args!("invalid type: {}, expected {}", unexp, exp))
}
Potential Solutions
- Add a new attribute for suggesting a type to dispatch off of. This can be brittle as it requires knowing the Deserialize implementation of each of the types within the untagged enum.
- Find a backwards-compatible way of checking each variant without a call to
invalid_type
- Find a backwards-compatible way of not allocating in
invalid_type
- Consider making this error not be
#[cold] (I'm assuming this will make little difference)
When profiling toml_edit, a hand-written
Deserializefor an untagged enum took a benchmark for a common case in cargo from 484us to 181us (for a singleCargo.tomlfile, using this in the dependency resolving would multiple that by hundreds).Root Cause
The derive's untagged enum implementation tries each variant, one after another.
For each failure, we generate an error which allocates its message and is adverised as a
#[cold]Potential Solutions
invalid_typeinvalid_type#[cold](I'm assuming this will make little difference)