-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Quadratic code size due to combination of ? and Drop for linear code #120604
Comments
As a workaround, you should get better results if you can structure the code like: let a = make()?;
let b = make()?;
let c = make()?;
Some([a,b,c]) (At least it works when applied to the following case.) Here's a self-contained example that reproduces the same quadratic growth: #![allow(improper_ctypes)]
pub struct NoisyDrop;
extern {
fn make() -> Option<NoisyDrop>;
fn extdrop(x: &mut NoisyDrop);
}
impl Drop for NoisyDrop {
fn drop(&mut self) {
unsafe {extdrop(self)}
}
}
#[inline(never)]
pub fn foo() -> Option<[NoisyDrop; 8]> {
unsafe {
Some(
[
make()?,
make()?,
make()?,
make()?,
make()?,
make()?,
make()?,
make()?,
]
)
}
} |
Thanks a lot for the hint. Edit: PR for my work-around: psychon/x11rb#914 I worked around this by using a `Vec` and `FromIterator, E> for Result`.let names = ["foo", "bar"]; // I left this out in my example above
let cookies: Result<Vec<_>, _> = names.into_iter().map(|name| send_request(name)).collect();
Ok(Cookies { cookies: cookies? }) This has the benefit of generating less code in the macro and thus should be way more obvious to optimise. This has the downside of requiring an extra allocation for the The only code using this (Also, I like your approach of working around the optimiser way more than mine. Nice. :-) ) |
I initially thought it was caused by drop-order, but the fields of the partially constructed struct or array are dropped in reverse order of construction, so I don't see any reason the compiler couldn't do a better job here. @rustbot label +T-compiler +C-optimization +I-heavy -needs-triage |
FYI clang does this for C++: (example from @psychon) struct HasDrop {
HasDrop();
~HasDrop();
};
void fn() {
HasDrop a0;
if (cond0) return;
HasDrop a1;
if (cond1) return;
// repeat for a2-a9
HasDrop a10;
if (cond10) return;
....
} will be lowered to something like this: void fn() {
HasDrop a0;
if (cond0) goto cleanup1;
HasDrop a1;
if (cond1) goto cleanup2;
// repeat for a2-a9
HasDrop a10;
if (cond10) goto cleanup;
....
cleanup:
drop(a10);
cleanup9:
drop(a9);
....
cleanup2:
drop(a1);
cleanup1:
drop(a0);
return;
} of course this is more complex in Rust's case since moved values in Rust doesn't need to have their |
Hi,
first: Sorry if this is a duplicate. I tried, but did not find anything.
There is a bug report for x11rb about code size. Some macro caused 111 KiB of code to be emited: psychon/x11rb#912
I tried to produce a self-contained example of what is going on and here is what I came up with the following
Playground link
(
#[inline(never)]
just exists so that I can read the resulting assembly; the original code does not use inline hints, but instead the functions are big enough not to be inlined. I'm not sure whetherdrop()
of theCookie
perhaps ends up being inlined and being part of the huge code size.)This code uses a macro, so just to make it clear what the problem is, here is the relevant function after macro expansion:
I expected to see this happen: Linear code size (= small code)
Instead, this happened: This results in quadratic code size due to the
drop
calls.Looking at the generated ASM in release mode and trying to translate this back into something rust-y, the code looks like this:
The actual code where this was observed has not just 5 cases (
a, b, c, d, e
) but 59 ones. Here, the quadratic behaviour starts to hurt a lot. I counted 1709 calls to functions withdrop_in_place
in their name.Meta
rustc --version --verbose
: Uhm... I tried this on the playground. But I can also reproduce this with my ancient version locally:The text was updated successfully, but these errors were encountered: