-
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
Migrate Cargo's &Option<T> into Option<&T> #14609
Conversation
r? @weihanglo rustbot has assigned @weihanglo. Use |
} | ||
|
||
fn with_precise(self, precise: &Option<Precise>) -> SourceId { | ||
if &self.inner.precise == precise { | ||
fn with_precise(self, precise: Option<Precise>) -> SourceId { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note that in this change the fn signature is changed to take the ownership - seems to be ok, but maybe less efficient?
} | ||
|
||
/// Creates a new `SourceId` from this source with the `precise` from some other `SourceId`. | ||
pub fn with_precise_from(self, v: Self) -> SourceId { | ||
self.with_precise(&v.inner.precise) | ||
self.with_precise(v.inner.precise.clone()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this seems wrong -- if both v
and self
are owned, why can't I take ownership of v.inner.precise
?
865e638
to
258425d
Compare
As part of this, also modify `Option<&String>` to `Option<&str>`, same for `PathBuf` Trying out my new lint rust-lang/rust-clippy#13336 - according to the [video](https://www.youtube.com/watch?v=6c7pZYP_iIE), this could lead to some performance and memory optimizations. Basic thoughts expressed in the video that seem to make sense: * `&Option<T>` in an API breaks encapsulation: * caller must own T and move it into an Option to call with it * if returned, the owner must store it as Option<T> internally in order to return it * Performance is subject to compiler optimization, but at the basics, `&Option<T>` points to memory that has `presence` flag + value, whereas `Option<&T>` by specification is always optimized to a single pointer.
Note that we intentionally run with very few clippy lints enabled and there are times we intentionally do what this PR changes away from because it actually makes the code better and a change like this is unlikely to have a significant impact on performance. This might make some parts nicer but having to make the connection between one API change and how it changes callers is hard when everything is bundled in one PR. |
@epage do you disagree with the arguments presented in the video? Or do you propose to simply break this PR into multiple ones to make reviewing easier? If so, could you give some guidance on the partitioning. Thx! |
The video is about API design and micro-optimizations without data to back it. Those are good baseline principles to operate on but they hardly map to every case in reality. In particular, Particularly when you get into
you hit limits. Yes, you can change I'm not against people generally improving this as they make changes (putting it into its own commit of course) or having tools call out it for the dev to reconsider (which is why I think we need a new lint level), but I don't want to dig into a 33 file PR to see if there are places this made it worse. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that we intentionally run with very few clippy lints enabled and there are times we intentionally do what this PR changes away from because it actually makes the code better and a change like this is unlikely to have a significant impact on performance.
To expand this a bit, without enabling the lint in Cargo's [lints]
table, we may need to do it again in the future. I would suggest at least not to do it at this moment, and revisit when the lint is in clippy officially.
Does that mean you're looking for crate maintainers' feekback for the lint, or you just want to understand how the new lint impacts a medium-size codecase? |
thx for feedback @weihanglo and @epage! The lint is part of |
Micro-benchmarking is notoriously hard to do right. I tried running this benchmark (assuming I got it done right), it shows about 11% difference. If I change use criterion::{criterion_group, criterion_main, Criterion};
#[inline(never)]
pub fn do_ref(val: &Option<Vec<usize>>) -> usize {
match val {
None => 42,
Some(val) => val.iter().sum(),
}
}
#[inline(never)]
pub fn do_as_ref(val: Option<&Vec<usize>>) -> usize {
match val {
None => 42,
Some(val) => val.iter().sum(),
}
}
fn criterion_benchmark(c: &mut Criterion) {
let data: Option<Vec<usize>> = Some(vec![0; 2]);
c.bench_function("ref", |b| b.iter(|| do_ref(&data)));
c.bench_function("as_ref", |b| b.iter(|| do_as_ref(data.as_ref())));
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches); |
To be clear, I'm less interested in micro-benchmarks because this kind of performance chance is unlikely to beat out the algorithmic complexity challenges in Cargo. |
☔ The latest upstream changes (presumably #14600) made this pull request unmergeable. Please resolve the merge conflicts. |
As part of this, also modify
Option<&String>
toOption<&str>
, same forPathBuf
Trying out my new lint rust-lang/rust-clippy#13336 - according to the video, this could lead to some performance and memory optimizations.
Basic thoughts expressed in the video that seem to make sense:
&Option<T>
in an API breaks encapsulation:&Option<T>
points to memory that haspresence
flag + value, whereasOption<&T>
by specification is always optimized to a single pointer.