-
Type: Improvement
-
Resolution: Unresolved
-
Priority: Unknown
-
None
-
Affects Version/s: None
-
Component/s: None
Compiling the tests with either cargo build or cargo test takes a long time. In a simple experiment, an incremental change to a string literal in the integration.rs file takes roughly 17s on my machine. From some initial investigation, this appears to be due to the code generated from serde Deserialize and Serialize derived implementation.
To arrive at this, I first used -Zself-profile on a cargo build that made the aforementioned incremental change. The results are attached in the "summary" file. Of note, the LLVM_module_codegen_emit_obj portion took up the vast majority of the time.
As a next step, I used cargo-llvm-lines to see what was causing most of the LLVM lines to be emitted. Unfortunately, cargo-llvm-lines doesn't currently support being run against unit tests, so I created an integration test that is similar to the one found in integration.rs (https://github.com/patrickfreed/mongo-rust-driver/blob/compile-time-investigation/tests/compile_time.rs). After running cargo-llvm-lines on that, I was able to observe that most of the generated code lived in the raw BSON serializer and deserializer. Note that this test alone builds pretty fast, so this is possibly not representative of the issue at hand.
Here are the top results:
Lines Copies Function name ----- ------ ------------- 1408384 (100%) 29997 (100%) (TOTAL) 96864 (6.9%) 52 (0.2%) bson::de::raw::Deserializer::deserialize_next 56325 (4.0%) 24 (0.1%) <bson::de::serde::BsonVisitor as serde::de::Visitor>::visit_map 34128 (2.4%) 21 (0.1%) <&mut bson::ser::raw::value_serializer::ValueSerializer as serde::ser::SerializeStruct>::serialize_field 26746 (1.9%) 50 (0.2%) bson::de::serde::Deserializer::deserialize_next 25957 (1.8%) 12 (0.0%) <bson::raw::serde::OwnedOrBorrowedRawBsonVisitor as serde::de::Visitor>::visit_map 22430 (1.6%) 24 (0.1%) <mongodb::operation::_::<impl serde::de::Deserialize for mongodb::operation::WriteResponseBody<T>>::deserialize::__Visitor<T> as serde::de::Visitor>::visit_map 20646 (1.5%) 118 (0.4%) bson::de::raw::Deserializer::access_document 16402 (1.2%) 59 (0.2%) bson::de::raw::Deserializer::deserialize_document 15542 (1.1%) 24 (0.1%) <mongodb::operation::_::<impl serde::de::Deserialize for mongodb::operation::CommandResponse<T>>::deserialize::__Visitor<T> as serde::de::Visitor>::visit_map 12931 (0.9%) 327 (1.1%) serde::de::Visitor::visit_map 12699 (0.9%) 121 (0.4%) bson::de::raw::DocumentAccess::read 10951 (0.8%) 12 (0.0%) <mongodb::error::_::<impl serde::de::Deserialize for mongodb::error::WriteConcernError>::deserialize::__Visitor as serde::de::Visitor>::visit_map 10159 (0.7%) 12 (0.0%) <mongodb::error::_::<impl serde::de::Deserialize for mongodb::error::BulkWriteError>::deserialize::__Visitor as serde::de::Visitor>::visit_map 9607 (0.7%) 12 (0.0%) <mongodb::operation::_::<impl serde::de::Deserialize for mongodb::operation::CursorInfo>::deserialize::__Visitor as serde::de::Visitor>::visit_map 8609 (0.6%) 150 (0.5%) core::result::Result<T,E>::map_err 8198 (0.6%) 82 (0.3%) bson::de::raw::CodeWithScopeDeserializer::read 7936 (0.6%) 42 (0.1%) <&mut bson::de::raw::BinaryDeserializer as serde::de::Deserializer>::deserialize_any
Given that we have a ton of Deserialize/Serialize derived types and use them in basically every code path, it seems likely that serde is the culprit here. We're not alone in this, serde causing compile time bloat is a well known problem (e.g. rust_analyzer has the same issue: https://github.com/serde-rs/serde/issues/1146#issuecomment-907622649).
There are some possible mitigations that we could explore here:
- Manually implement Serialize/Deserialize for our custom types
- It seems like the derived implementations generate a ton of code, so making sparse manual implementations could help here.
- Incorporate changes into the serializer / deserializer themselves to cut back on the amount of monomorphization that is happening
- existing attempt at this in serde_json: https://github.com/serde-rs/json/pull/745
- Stop using serde for internal types
- We would continue to rely on serde to deserialize user provided Ts, but we could skip on using it for known command shapes and error responses, for example.
There may be other mitigations as well, or different causes that this investigation didn't reveal. I think this is probably a good starting point for trying something though.
One thing that's important to note is that these compile time issues are not exclusive to our tests, they just so happen to be the one that us maintainers run up against the most. I think any app that uses the driver heavily will probably incur similar costs as well.
- is related to
-
RUST-570 Test suite compile times are extremely slow
- Closed