-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
Add an option to optimize just dependencies #1359
Comments
Would this apply to the standard library as well? (I.E. compiling your code in debug mode while the standard lib would be compiled in release mode) |
Unfortunately no, the standard library currently only comes as optimized. |
Oh, excellent. That's actually what I was hoping would happen, as then most of the bottlenecks are going to be in my code that calls the std lib rather than in the std lib itself. |
+1. I want this for Servo |
Inside the `dependencies.foo` section, you may now specify a minimum optimization level to be applied to this dependency. If multiple such optimization levels are supplied, Cargo chooses the highest one. This will probably need to go through an RFC before landing. Addresses rust-lang#1359.
@alexcrichton I think I can work on this. From the design point of view, we can add an
which may be better then the current default of slow dependencies. These two options are somewhat orthogonal, and it should be possible to implement one first and add the other later. Another design question is what exactly a dependency is? If you have some path dependencies, do you want them to be optimized? And what about overridden dependencies? I can suggest two solutions here. A package is not a dependency if it is
From the implementation point of view I hope that the only thing that should be changed is |
@matklad awesome! I think that this definitely has a bit of a design aspect to it before charging ahead, although I think you're correct in that the function you highlighted is the only one that needs to change (convenient, eh?). I like the idea of [profiles.dev]
dependency-profile = 'release' or something like that. Basically you select a profile for dependencies rather than any specific information about them. If we later add the ability to specify custom profiles that could also be selected. I also agree about your heuristic about what a dependency is and what isn't. In general I think that all path dependencies are just implementation details (or units of incrementality) that are part of the current crate being worked on. That being said, however, the name "dependency" may be a bit misleading there because they are indeed dependencies. I wonder if perhaps the term "upstream" could be used? That may help distinguish "other people's code" from "my local code". The term "upstream" and "downstream" are sometimes conflated, though, so that may not be best... And all that being said you may still want to optimize path dependencies. For example if you temporarily override a perf-critical dependency to a local crate, you probably still want to optimize it even though you're working on it. I think that I might somewhat lean towards "dependency" meaning "anything not the root package". We could eventually support something where you can specify profiles for specific dependencies in a |
Yes, I think it is the best approach.
I find "upstream" even more confusing than "dependency". What about "local package"?
I'm more for "dependency = anything non local". The original motivation for "optimize dependencies" is that you build them only once, so it makes sense to spend more time on compiling them efficiently. Local packages however are rebuild more often (n times) then deps (1 time), and optimizing them will affect compile times negatively. If you really need fast local packages the right solution might be to add Out of curiosity, how this will work with monomorphisation? If I have a template function in a dependency compiled in release mode, and use it from a crate compiled in debug mode, will I get any benefits? Suppose that the templated function does not call other non-templated functions. |
"local" does seem kinda on the right track, yeah, although it may be kind of odd saying: [profile.dev]
non-local-dependency-profile = 'release' Quite a mouthful! This will indeed not really work well with monomorphization, all instantiations will just be optimized at the same level as the crate they're instantiated into. |
Maybe |
Hm, in this piece of TOML two ideas are expressed:
I totally agree with 1, but 2 does not feel right: ideally profile should be set per package, but What about [profile-overrides.dev]
vendor-dependencies="release" ? This can in future be extended to handle more fine-grain overrides. |
Hm yeah I guess if you set all dependencies to a release profile you'd want that to happen in both test and dev. Although we currently have two profiles for that, so any configuration in one already needs to be reflected in another, so maybe it's not so bad? I'd be somewhat wary of inventing new top-level keys like |
I'd like to ask what exactly a profile is? I mean, there is Here is a model I have in mind for this feature: A profile is basically a description of a set of flags, applied during compilation of a package. And there is "compilation mode" which determines what profiles are applied to what packages. Like, there is As far as I understand, currently in cargo the notion of compilation mode is not present first class. For example, in [profile.test]
opt-level = 3 in |
Ah yeah to clarify, when I say profile I mean what you're writing down in Cargo.toml like That being said, though, my motivation of |
Yes, if we specify override globally, like
Likely not, if we use something like
|
@alexcrichton what about
|
Yeah we could in theory support a top-level "always override dependencies with this profile", but I suspect that it's likely to be a local decision per-profile (especially if we grow custom profiles) rather than an always-true option. Note that the dev/test profile management here may want to be improved... I can also see the "dependency = anything non local" logic, yeah, although I think it can work both ways. You may sometimes be frobbing all the path dependencies at once, but there could also be legitimate cases where only the main one is being modified. I suspect though that the "anything non local" case is more common. At least in terms of performance, I would expect any bottlenecked dependencies to all be upstream rather than being worked on locally. |
There are some concerns on #2380, outlining few tensions to balance when implementing this. |
@alexcrichton I agree with your analysis. One thing I want to add is that there is a possible case of optimizing (O1?) dependencies by default, which still can (maybe) bring some benefits. And for the future refence I've done some dirty "benchmarks" of the approach in #2380. I've benchmarked this crate (this crate was the reason I found this issue :) ). It does some OpenGL rendering in a super inefficient and unidiomatic way. The major run time bottleneck is texture loading (which is done by a library). So here are the measurements of a recompile cycle after a trivial whitespace edit in "src/lib.rs" and a corresponding time to see something rendered. Usual debug build
Usual --release build
Debug build with release deps
Debug build with opt-level=1 deps
Debug build with opt-level=1 for everything
I kinda like how compile and run time coincide in the third case =) |
Thanks for the data @matklad! Out of curiosity, what do the numbers look like if |
@alexcrichton updated the table |
Oh sorry, I meant for O1 that the entire graph had opt-level 1, not just the dependencies. Does that reduce the runtime at all? |
yep, updated the table. |
Thanks! Looks like that kinda confirms that there's a "sweet spot O1" which may suffice for some build configurations? |
Not sure. First of all, I don't think that this benchmarks are really representative (target crate is just some quick an dirty openGL exercises, nothing close to production). In particular, target crate is pretty small, and I suppose that compile time grows lineary with code size, while run time is sub linear. That is, for large code bases, And given current largish compile times, I won't feel comfortable sacrificing compile time for anything at all :) |
@mkovacs currently, no, there hasn't been much progress on this. This feature remains unimplemented but @matklad sounds like he's got thoughts above |
Yeah, this is basically blocked on profiles, and I have not yet written a pre RFC because I don't know what to write in the "proposed solution" section, but I'll link my current take on the problem formulation: #4140 (comment) |
Could profiles be separated from an overall option to build dependencies as release? For example, the new default could be debug local code + release crate dependencies with a |
I think we should fix the issue in two steps:
|
Is there any workaround currently to make one of the dependencies to build in release mode (optimized, without debug info)? I have one dependency that is way slower in debug than in release. |
@ozkriff @not-fl3 yesterday at Rust gamedev meetup at Saint Petersburg you've both said that this feature would be very useful for gamedev. So I've decided to update my PR so that you can check if this feature indeed brings the benefits you expect (there's a fear that due to monomorphization optimization level for deps might be irrelevant). Given that we now have unstable cargo features, I think that we could probably land this in some unstable form. For sure, we won't stabilize anything close to my implementation, because profiles need to be fixed first. However, this won't probably happen soon, so, if this is useful, we can try to help at least nightly users. As this only affects the development experience of binary crates authors, I don't think there's a high risk behind adding this under a feature flag. To try this out, install Cargo from this branch: https://github.com/matklad/cargo/tree/fast-dependencies-2
Then add to your Cargo.toml (for workspaces this must be a non-virtual manifest):
|
Perhaps rustc could compile monomorphized code from external crates according to the optimization flags given when those crates were built, distinct from the flags applying to the current crate? |
@matklad It works beautifully for my use cases! :-D |
@matklad great work! Made a quick hack not-fl3@fd2a0b5 to cover all of my needs. Its a way to force even local dependencies to be built in release mode. Two use cases I wanted to cover:
For example, local mesh loading crate. Its not on crates.io, so its added as a local lib. But in the same time its not activly developed, so its really nice to compile it once in release.
For example, local server with entrypoint in lib.rs. Nice to have an option to make it fast during development of client. It would be really great to have something like this in nightly cargo! |
I havent read anything about monophormization, but from my point of view the only thing that need to be like |
I also support the idea of specifying optimization level for individual dependencies. I'm developing a program that deals with images, and it's a major pain to test it, because the |
@Manishearth proposed an RFC: rust-lang/rfcs#2282 |
As there hasn't been any activity here in over 6 months I've marked this as stale and if no further activity happens for 7 days I will close it. I'm a bot so this may be in error! If this issue should remain open, could someone (the author, a team member, or any interested party) please comment to that effect? The team would be especially grateful if such a comment included details such as:
Thank you for contributing! (The cargo team is currently evaluating the use of Stale bot, and using #6035 as the tracking issue to gather feedback.) If you're reading this comment from the distant future, fear not if this was closed automatically. If you believe it's still an issue please leave a comment and a team member can reopen this issue. Opening a new issue is also acceptable! |
This has been implemented on nightly and is tracked at rust-lang/rust, so I'm going to close in favor of those locations. |
@alexcrichton Your "implemented" link goes to the docs for profiles in |
@SimonSapin I think the link should be: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#profile-overrides The |
Oh I see, never mind then :) |
I found my way here while wondering if this feature existed, and since this is a years-old thread, looks like the feature is no longer "unstable", and docs are now here: https://doc.rust-lang.org/nightly/cargo/reference/profiles.html#overrides |
The package being built (and all targets within) will not be built in release mode, but all dependencies would be built in release mode.
cc #784
cc #784 (comment)
The text was updated successfully, but these errors were encountered: