Skip to content

Use cold functions for panic formatting Option::expect, Result::unwra… #31116

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

Merged
merged 2 commits into from
Jan 23, 2016

Conversation

bluss
Copy link
Member

@bluss bluss commented Jan 22, 2016

Use cold functions for panic formatting Option::expect, Result::unwrap, expect

These methods are marked inline, but insert a big chunk of formatting
code, as well as other error path related code, such as
deallocating a std::io::Error if you have one.

We can explicitly separate out that code path into a function that is
never inline, since the panicking case should always be rare.

@rust-highfive
Copy link
Contributor

r? @nikomatsakis

(rust_highfive has picked a reviewer for you, use r? to override)

@bluss
Copy link
Member Author

bluss commented Jan 22, 2016

Playpen code to compare these in artificial setting: https://play.rust-lang.org/?gist=94e1ab9ed151c75a5a97&version=nightly

Option::unwrap is already good. Look at the optimized versions of option_expect and option_expect_new to compare them. option_expect_new now works just like Option::unwrap does, with less code even in the Some case too.

@nagisa
Copy link
Member

nagisa commented Jan 22, 2016

Looks pretty good to me (if not clear yet: I really, really like this :)). For Result::expect this saves ~80 instructions (improvement around the 65% mark) for the optimised assembly in the call location.

EDIT: still needs same treatment for Result::unwrap_err

@nikomatsakis
Copy link
Contributor

Seems like a good idea to me. (Though I still dislike Option::expect with the passion of...well, not that much passion really. But a little.)

@bluss
Copy link
Member Author

bluss commented Jan 22, 2016

With this change I might even become an Option.expect() convert.

#[cold]
fn expect_failed(msg: &str) -> ! {
panic!("{}", msg)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be moved outside of Option<T>? That should mean it's not monomorphized and should be even smaller I believe

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, didn't think of that. It applies to everything I think.

@alexcrichton
Copy link
Member

Nice ideas! I had a few more other ideas about monomorphization and seeing if we can't codegen these in libstd, but otherwise I'm always excited about changes like these.

@bluss
Copy link
Member Author

bluss commented Jan 22, 2016

Maybe Result's unwrap_failed<E> should move out of the impl block too, is it unnecessarily monomorphized per T otherwise?

@brson
Copy link
Contributor

brson commented Jan 22, 2016

Neat find.

@arthurprs
Copy link
Contributor

👏 I like this

@@ -685,7 +685,7 @@ impl<T, E: fmt::Debug> Result<T, E> {
match self {
Ok(t) => t,
Err(e) =>
panic!("called `Result::unwrap()` on an `Err` value: {:?}", e)
unwrap_failed("called `Result::unwrap()` on an `Err` value: {:?}", e),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the trailing {:?} here can be removed

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(same below as well

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's supposed to be more consistent with a trailing comma for every case. Not sure what rustfmt usually does.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh.. reading.. how do I?

I see. That's an important change.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I think that the trailing : {:?} part of the string can be removed in both cases, that's covered by the format string at the very bottom

@bluss
Copy link
Member Author

bluss commented Jan 22, 2016

I will rebase to squash all the fixups together into one commit.

bluss added 2 commits January 22, 2016 19:06
…p etc

Option::expect, Result::unwrap, unwrap_err, expect

These methods are marked inline, but insert a big chunk of formatting
code, as well as other error path related code, such as deallocating
a std::io::Error if you have one.

We can explicitly separate out that code path into a function that is
never inline, since the panicking case should always be rare.
Move functions out of their impl blocks; avoids unnecessary
monomorphization by type parameters that are irrelevant to the message
output.
@bluss
Copy link
Member Author

bluss commented Jan 22, 2016

r? @alexcrichton

This is done, unless we want to measure the difference of something a bit more interesting. I don't really have the resources to build all of rust multiple times though.

@alexcrichton
Copy link
Member

@bors: r+ 257bff3

bors added a commit that referenced this pull request Jan 23, 2016
Use cold functions for panic formatting Option::expect, Result::unwrap, expect

These methods are marked inline, but insert a big chunk of formatting
code, as well as other error path related code, such as
deallocating a std::io::Error if you have one.

We can explicitly separate out that code path into a function that is
never inline, since the panicking case should always be rare.
@bors
Copy link
Collaborator

bors commented Jan 23, 2016

⌛ Testing commit 257bff3 with merge d63b8e5...

@bors bors merged commit 257bff3 into rust-lang:master Jan 23, 2016
@bluss bluss deleted the expect-out-cold branch January 23, 2016 08:41
@hudson-ayers
Copy link
Contributor

I realize this is a very old PR, but I wanted to point out one downside of this change:

With this change, for expect() messages, even if your panic handler does nothing (i.e. panic_immediate_abort), the messages passed to expect() are still embedded in your binary.

Consider the two following code snippets:

fn check(opt: Option<u32>) -> u32 {
    opt.expect("This option should always be Some by the time this function is called.")
}
fn check(opt: Option<u32>) -> u32 {
    match opt {
        Some(val) => return val,
        None => panic!("This option should always be Some by the time this function is called.")
    }
}

I would typically assume these two functions should be identical, but in a binary with no panic handler only the former pays the code size cost of the message being embedded in the binary.

Of course, in a binary that has many calls to expect() and does have an actual panic handler, this change generates substantial savings. But in severely size-constrained binaries with no panic handler, it is a shame that messages passed to expect() cannot be optimized out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants