-
-
Notifications
You must be signed in to change notification settings - Fork 789
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Revert the suggestion to use the derive
feature instead of serde_derive
directly
#2584
Comments
I've planned this for a long time but it's stuck on rust-lang/rust#54363 (comment). By depending separately on serde and serde_derive you no longer get this version equality constraint, which causes mayhem: for example Dependabot will try to upgrade them independently. One version of serde_derive is specific to exactly one version of serde, because it references private APIs that serde exposes specifically for that version of the derive. With first-class macro dependencies, we'd want to add: [package]
name = "serde_derive"
version = "1.0.Z"
[lib]
proc-macro = true
[dependencies]
# ...
[macro-dependencies]
serde = "=1.0.Z" which correctly encodes the version equality constraint. |
Dependabot is actually able to correctly upgrade crates in pairs (or more) for other languages, such as JavaScript. Not sure if they require some sort of special support from the package manager like you suggested or they can detect it automatically. It might be a reasonably easily PR to Dependabot to support that. I'll try to look further into it. But yeah having a way to specify crates that need to be in lock step would be really good too for other tooling. wasmtime for example has various supporting crates (for WASI and co.) that also always get bumped in lock step, so having a way to specify that would be great too. |
why not introduce a new crate [dependencies]
serde_lockstep = "1"
serde = "1"
serde_derive = "1" |
This should be mentioned on the |
i suppose this could be further improved by migrating the meat and potatoes of |
I think I have a horrible idea here! In [package]
name = "serde"
version = "1.0.101"
[target.'cfg(never)'.dependencies]
serde-derive = { version = "=1.0.101", path = "./serde-derive" } If I understand Cargo's innner workings correctly, this "associated macro" pattern would constrain Cargo to always pick serde and serde derive in lockstep, without putting serde-derive in front of serde in compilation graph. That is, serde derive would still be present in lockfile, with a correct version, but it won't be otherwise downloaded or built. Light testing with https://github.com/matklad/macro-dep-test seems to work! |
Did you test from outside the workspace? I've found that deactivated dependencies version requirements (using |
I am quite interested in the (sorry if that is off-topic 😆) |
@Nemo157 seems to work as (perhaps not) intended?
That's the thing! optional dependencies are not present in the lockfile --- Cargo knows what features exist in the root package being built, so it can correct infer that some transitive feature in a dependency can not be activated, and so this dep can be pruned from Cargo.lock. With |
perhaps we could change it to |
Pushed an explanation to https://github.com/matklad/macro-dep-test/blob/master/README.md |
Thanks @matkad (#2584 (comment)) and @soqb (#2584 (comment)), that looks great. I didn't think of that. I would be open to shipping that solution in serde. |
im working on a PR now, trying to get something for CI to guarantee it! |
Alternatively (or in addition), pull out a
I've done something similar in clap and its worked out well. |
^^ that is what |
One possibly big problem with suggesting users to go back to depending on Maybe this is solved nowadays, I guess rustc should be able to tell that the macro-namespace If this problem still persists, I think that's a good reason to favor the solution suggested by @epage (I personally prefer if anyways because of convenience and users not having to do anything for the compile-time gains). |
I wonder if it would be possible to at least detect faulty crate dependency resolutions by having const pub SERDE_VERSION_CHECK: () = {
if SERDE_VERSION.0 != 1 || SERDE_VERSION.1 != 0 || SERDE_VERSION.2 != 175 {
panic!("serde_derive and serde version mismatch detected");
}
}; Then at least users would be left less confused about what to do when the versions get out of sync, and it would be guaranteed to fail immediately rather than only if there happened to be an actual conflict. |
No, this is still not solved. A workaround is to use |
By not activating the `derive` feature on `serde`, the compilation speed can be improved by a lot. This is because `serde` can then compile in parallel to `serde_derive`, allowing it to finish compilation possibly even before `serde_derive`, unblocking all the crates waiting for `serde` to start compiling much sooner. As it turns out the main deciding factor for how long the compile time of a project is, is primarly determined by the depth of dependencies rather than the width. In other words, a crate's compile times aren't affected by how many crates it depends on, but rather by the longest chain of dependencies that it needs to wait on. In many cases `serde` is part of that long chain, as it is part of a long chain if the `derive` feature is active: `proc-macro2` compile build script > `proc-macro2` run build script > `proc-macro2` > `quote` > `syn` > `serde_derive` > `serde` > `serde_json` (or any crate that depends on serde) By decoupling it from `serde_derive`, the chain is shortened and compile times get much better. Check this issue for a deeper elaboration: serde-rs/serde#2584 For `samply` I'm seeing a reduction from 5.43s to 3.70s when compiling `fxprof-processed-profile` in `release` mode.
By not activating the `derive` feature on `serde`, the compilation speed can be improved by a lot. This is because `serde` can then compile in parallel to `serde_derive`, allowing it to finish compilation possibly even before `serde_derive`, unblocking all the crates waiting for `serde` to start compiling much sooner. As it turns out the main deciding factor for how long the compile time of a project is, is primarly determined by the depth of dependencies rather than the width. In other words, a crate's compile times aren't affected by how many crates it depends on, but rather by the longest chain of dependencies that it needs to wait on. In many cases `serde` is part of that long chain, as it is part of a long chain if the `derive` feature is active: `proc-macro2` compile build script > `proc-macro2` run build script > `proc-macro2` > `quote` > `syn` > `serde_derive` > `serde` > `serde_json` (or any crate that depends on serde) By decoupling it from `serde_derive`, the chain is shortened and compile times get much better. Check this issue for a deeper elaboration: serde-rs/serde#2584 For `samply` I'm seeing a reduction from 5.43s to 3.70s when compiling `fxprof-processed-profile` in `release` mode.
By not activating the `derive` feature on `serde`, the compilation speed can be improved by a lot. This is because `serde` can then compile in parallel to `serde_derive`, allowing it to finish compilation possibly even before `serde_derive`, unblocking all the crates waiting for `serde` to start compiling much sooner. As it turns out the main deciding factor for how long the compile time of a project is, is primarly determined by the depth of dependencies rather than the width. In other words, a crate's compile times aren't affected by how many crates it depends on, but rather by the longest chain of dependencies that it needs to wait on. In many cases `serde` is part of that long chain, as it is part of a long chain if the `derive` feature is active: `proc-macro2` compile build script > `proc-macro2` run build script > `proc-macro2` > `quote` > `syn` > `serde_derive` > `serde` > `serde_json` (or any crate that depends on serde) By decoupling it from `serde_derive`, the chain is shortened and compile times get much better. Check this issue for a deeper elaboration: serde-rs/serde#2584 For `wasmtime` I'm seeing a reduction from 24.75s to 22.45s when compiling in `release` mode. This is because wasmtime through `gimli` has a dependency on `indexmap` which can only start compiling when `serde` is finished, which you want to happen as early as possible so some of wasmtime's dependencies can start compiling. To measure the full effect, the dependencies can't by themselves activate the `derive` feature. I've upstreamed a patch for `fxprof-processed-profile` which was the only dependency that activated it for `wasmtime` (not yet published to crates.io). `wasmtime-cli` and co. may need patches for their dependencies to see a similar improvement.
By not activating the `derive` feature on `serde`, the compilation speed can be improved by a lot. This is because `serde` can then compile in parallel to `serde_derive`, allowing it to finish compilation possibly even before `serde_derive`, unblocking all the crates waiting for `serde` to start compiling much sooner. As it turns out the main deciding factor for how long the compile time of a project is, is primarly determined by the depth of dependencies rather than the width. In other words, a crate's compile times aren't affected by how many crates it depends on, but rather by the longest chain of dependencies that it needs to wait on. In many cases `serde` is part of that long chain, as it is part of a long chain if the `derive` feature is active: `proc-macro2` compile build script > `proc-macro2` run build script > `proc-macro2` > `quote` > `syn` > `serde_derive` > `serde` > `serde_json` (or any crate that depends on serde) By decoupling it from `serde_derive`, the chain is shortened and compile times get much better. Check this issue for a deeper elaboration: serde-rs/serde#2584 For `wasmtime` I'm seeing a reduction from 24.75s to 22.45s when compiling in `release` mode. This is because wasmtime through `gimli` has a dependency on `indexmap` which can only start compiling when `serde` is finished, which you want to happen as early as possible so some of wasmtime's dependencies can start compiling. To measure the full effect, the dependencies can't by themselves activate the `derive` feature. I've upstreamed a patch for `fxprof-processed-profile` which was the only dependency that activated it for `wasmtime` (not yet published to crates.io). `wasmtime-cli` and co. may need patches for their dependencies to see a similar improvement.
By not activating the `derive` feature on `serde`, the compilation speed can be improved by a lot. This is because `serde` can then compile in parallel to `serde_derive`, allowing it to finish compilation possibly even before `serde_derive`, unblocking all the crates waiting for `serde` to start compiling much sooner. As it turns out the main deciding factor for how long the compile time of a project is, is primarly determined by the depth of dependencies rather than the width. In other words, a crate's compile times aren't affected by how many crates it depends on, but rather by the longest chain of dependencies that it needs to wait on. In many cases `serde` is part of that long chain, as it is part of a long chain if the `derive` feature is active: `proc-macro2` compile build script > `proc-macro2` run build script > `proc-macro2` > `quote` > `syn` > `serde_derive` > `serde` > `serde_json` (or any crate that depends on serde) By decoupling it from `serde_derive`, the chain is shortened and compile times get much better. Check this issue for a deeper elaboration: serde-rs/serde#2584 For `wasmtime` I'm seeing a reduction from 24.75s to 22.45s when compiling in `release` mode. This is because wasmtime through `gimli` has a dependency on `indexmap` which can only start compiling when `serde` is finished, which you want to happen as early as possible so some of wasmtime's dependencies can start compiling. To measure the full effect, the dependencies can't by themselves activate the `derive` feature. I've upstreamed a patch for `fxprof-processed-profile` which was the only dependency that activated it for `wasmtime` (not yet published to crates.io). `wasmtime-cli` and co. may need patches for their dependencies to see a similar improvement.
This should help with untangling serde's serialized build. Addresses serde-rs#2584
It's also possible to always import the traits from |
By not activating the `derive` feature on `serde`, the compilation speed can be improved by a lot. This is because `serde` can then compile in parallel to `serde_derive`, allowing it to finish compilation possibly even before `serde_derive`, unblocking all the crates waiting for `serde` to start compiling much sooner. As it turns out the main deciding factor for how long the compile time of a project is, is primarly determined by the depth of dependencies rather than the width. In other words, a crate's compile times aren't affected by how many crates it depends on, but rather by the longest chain of dependencies that it needs to wait on. In many cases `serde` is part of that long chain, as it is part of a long chain if the `derive` feature is active: `proc-macro2` compile build script > `proc-macro2` run build script > `proc-macro2` > `quote` > `syn` > `serde_derive` > `serde` > `serde_json` (or any crate that depends on serde) By decoupling it from `serde_derive`, the chain is shortened and compile times get much better. Check this issue for a deeper elaboration: serde-rs/serde#2584 For `wasmtime` I'm seeing a reduction from 24.75s to 22.45s when compiling in `release` mode. This is because wasmtime through `gimli` has a dependency on `indexmap` which can only start compiling when `serde` is finished, which you want to happen as early as possible so some of wasmtime's dependencies can start compiling. To measure the full effect, the dependencies can't by themselves activate the `derive` feature. I've upstreamed a patch for `fxprof-processed-profile` which was the only dependency that activated it for `wasmtime` (not yet published to crates.io). `wasmtime-cli` and co. may need patches for their dependencies to see a similar improvement.
Here it was decided that
serde_derive
should not be imported directly and instead thederive
feature should be used. This however is directly responsible to the incredibly bad compile times that the new prebuilt binary is meant to address. Here's why:Cargo itself is able to heavily parallelize compilation, however
serde
with thederive
feature active, causes a huge non-parallelizable dependency chain:proc-macro2
compile build script >proc-macro2
run build script >proc-macro2
>quote
>syn
>serde_derive
>serde
>serde_json
(or any crate that depends on serde)This means that while cargo can compile lots of crates in parallel in the beginning, all the crates depending on
serde
can only start compiling afterserde
is done compiling, which is very late. So there might be a reasonably long time where cargo isn't able to compile much in parallel at all, resulting in few cores being utilized.This can be seen here:
However, you can easily break this dependency chain, by not activating the
derive
and instead depending onserde_derive
yourself. This allowsserde
and its dependents likeserde_json
and a made upneeds-serde
in my example to compile from the beginning of the compilation, completely in parallel to all the derive related crates.This cuts compile times from 4.7s to 2.6s (and honestly I don't even know where the dubious claim of 9s even came from, the serde with
derive
feature release build takes 3.8s in this scenario (excluding everything that comes after)).So this basically gives you the same, if not better compile time performance improvements than the prebuilt binary in almost every case:
serde
has no dependency on any derive crate at all, allowing it to start compiling even earlier than with a prebuilt binary.syn
is still a part of the dependency chain, which realistically is going to be the case for most real world projects anyway, as other derive crates will need it to be built anyway, so the prebuilt binary would only help in mostly toy projects anyway.The text was updated successfully, but these errors were encountered: