Conversation
|
(rust_highfive has picked a reviewer for you, use r? to override) |
|
Mostly a drive-by comment, but I wonder if we should use strings for features at all... A natural idea is to enumerate all features in a package, and then use a number as a feature identifier. That way, a set of features could be stored as a bitset and fit into a single u64 most of the time.... |
|
@matklad So I see one small problem with that, If I require |
alexcrichton
left a comment
There was a problem hiding this comment.
Thanks! The wins here are quite nice :)
Can you confirm that this doesn't regress the Servo use case much? In the sense of "big pre-resolved dependency graph still executes quickly".
Other than that I'm mildly worried about the memory leaks here. Cargo is often used as a library and I could imagine that if you're doing lots of resolutions of lots of crates the memory could get a little out of hand here over time. I wonder if instead, though, we could have a scheme like:
- There's an
Internerstructure which is used to get/set strings - Each
Configstores anInterner - There's a scoped thread local
Internerset at the start of each Cargo thread, maybe byConfig?
It'd be a bit trickier to get the interner to other threads in Cargo's backend but I think it's plausible in the long run even with an Interner struct. For now I think this'd look like a scoped thread local during resolve (aka destroyed entirely after resolve is done).
Does that make sense though? Or do you think the static/leak strategy would still be prefererable?
src/cargo/core/interning.rs
Outdated
There was a problem hiding this comment.
Could the Ord and PartialOrd references be left out here? I think b/c they're comparing strings rather than contents it may end up being confusing down the road.
|
I don't really know what I think about having a The current implementation only uses If we make I did not understand your suggestion about So overall I don't know. |
|
Ok good to know on the resolve time! Sorry I think I wasn't clear before, but on second thought it's probably not worth it to have destruction here and it's probably best to just have this be a global. We can always work with it later and tweak it as necessary. If it's global though we can for sure never delete it, so want to go ahead and add a |
|
Just to confirm you are leaning toward using the global/leek strategy, and you are asking me to add code that uses unsafe to convert an Next time I get a chance I will play with some of the options, and put links hear for us to compare. |
|
Ah yeah I'm coming around to a global interner which leaks everything you add to it. I do like the idea of |
|
Note: that all the performance number should be rethought in lite of #5132 If we are cloning less often than improvements to performance of cloning will matter less. I assume that if two |
|
Holy cow, nice! |
In a test on rust-lang#4810 (comment) Before we got to 5000000 ticks in ~72 sec After we got to 5000000 ticks in ~65 sec
In a test on rust-lang#4810 (comment) Before we got to 5000000 ticks in ~65 sec After we got to 5000000 ticks in ~52 sec
|
A nice to you. You are the one that spotted and identified the problem! I didn't notice that the clones I was optimizing where not supposed to be there. I rebased and clean up this PR. Remarkably, it still pulls its weight! Next to try a version with |
|
I have worked on the scoped/ I have threaded it thru to some of the places that need it, |
|
Thanks for trying that out! I think it's clear the lazy_static approach will do fine for now. If you additionally don't see lock contention in the profile then I think it's for sure a fine approach. |
|
The However the fact that this can hand out Witch brings us to: 5000000 ticks in ~48 sec, 0r 104k ticks/sec |
|
Ok sounds good to me! And to be clear it's not that we want no unsafe code in Cargo we just want very little :). Cargo itself has a fair bit of transitive unsafe code |
|
I tried interning the features: If we want the speed of Eh2406@411355a and dont want the One way or the other, I am feeling ready to move onto the next avenue of attack. |
|
Ah yeah I'm fine incrementally improving the situation here rather than trying to do it all at once. Did you want to put some more changes in this PR and then I can r+? |
src/cargo/core/interning.rs
Outdated
There was a problem hiding this comment.
One thing this may actually want to do is call s.into_boxed_str() as that'll call shink_to_fit and make sure we're not hanging on to any lingering bytes, but that can happen in a follow-up!
There was a problem hiding this comment.
let boxed = s.into_boxed_str();
let ptr = boxed.as_ptr();
let len = boxed.len();
mem::forget(boxed);
Yes?
There was a problem hiding this comment.
indeed!
Eventually this could even use Box::leak :)
src/cargo/core/interning.rs
Outdated
There was a problem hiding this comment.
Could this be listed as an implementation of Deref to get all the fun methods on strings?
There was a problem hiding this comment.
Yes, thou they do not hash the same. I can use Deref instead of to_inner if you prefer.
There was a problem hiding this comment.
Ah yeah I wouldn't worry too much about that
|
Looks great! I'd like to get the |
|
Er actually, there's also a bunch of test failures on Windows at least :( |
|
I removed the last optimization as I don't know how it was causing the test failures. And added your suggestions. I think let us merge this, and I'll rework the last commit as a separate PR. |
|
@bors: r+ |
|
📌 Commit 98480e8 has been approved by |
String interning This builds on the work from #5118. This interns the strings in the part of resolver that gets cloned a lot. In a test on #4810 (comment) Before we got to 1700000 ticks in ~(63 to 67) sec from #5118 After we got to 1700000 ticks in ~(42 to 45) sec The interning code itself would be much better with a `leak` function that converts a `String` to a `&'static str`. Something like: ```rust pub fn leek(s: String) -> &'static str { let ptr = s.as_ptr(); let len = s.len(); mem::forget(s); unsafe { let slice = slice::from_raw_parts(ptr, len); str::from_utf8(slice).unwrap() } } ``` but "there is no unsafe in Cargo", and I am not the best at unsafe. So I just `to_string` and lived with the extra copy. Is there a better way to hand out references? I assumed that `InternedString::new` world start appearing in profile result, and that we would want `PackageId`, and `Summary`, Et Al. to store the `InternedString`. That is why I put the interner in a shared folder. So far it is just used in the resolver. It may make sense for a lot more of the Strings to be interned, but with the extra copy... I have not explored it yet.
|
☀️ Test successful - status-appveyor, status-travis |
restructure `Activations` for better clone This builds on the work in #5121 When we last met we had: 5000000 ticks in ~48 sec, 0r 104k ticks/sec This small change brings us to: 5000000 ticks in ~21 sec, 0r 238k ticks/sec Edit: sorry for the large diff only the last commit is new. The rest are from #5121
This builds on the work from #5118. This interns the strings in the part of resolver that gets cloned a lot.
In a test on #4810 (comment)
Before we got to 1700000 ticks in ~(63 to 67) sec from #5118
After we got to 1700000 ticks in ~(42 to 45) sec
The interning code itself would be much better with a
leakfunction that converts aStringto a&'static str. Something like:but "there is no unsafe in Cargo", and I am not the best at unsafe. So I just
to_stringand lived with the extra copy. Is there a better way to hand out references?I assumed that
InternedString::newworld start appearing in profile result, and that we would wantPackageId, andSummary, Et Al. to store theInternedString. That is why I put the interner in a shared folder. So far it is just used in the resolver. It may make sense for a lot more of the Strings to be interned, but with the extra copy... I have not explored it yet.