Skip to content
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

Result<T, E> where size_of::<T>() < 128 and size_of::<E> >= 128 #4652

Closed
llogiq opened this issue Oct 11, 2019 · 3 comments · Fixed by #9373
Closed

Result<T, E> where size_of::<T>() < 128 and size_of::<E> >= 128 #4652

llogiq opened this issue Oct 11, 2019 · 3 comments · Fixed by #9373
Labels
A-lint Area: New lints E-medium Call for participation: Medium difficulty level problem and requires some initial experience. L-perf Lint: Belongs in the perf lint group

Comments

@llogiq
Copy link
Contributor

llogiq commented Oct 11, 2019

Those will cause LLVM to issue memcpy calls to copy them around. Boxing the error type will push them below LLVM's size threshold, thus possibly improving performance, unless the Err case is more likely than the Ok case.

@llogiq llogiq added E-medium Call for participation: Medium difficulty level problem and requires some initial experience. A-lint Area: New lints L-perf Lint: Belongs in the perf lint group labels Oct 11, 2019
@andjo403
Copy link

feels like the large_enum_variant check but with a limit of 128 by default instead of 200.

@llogiq
Copy link
Contributor Author

llogiq commented Oct 14, 2019

Sort of, but specialized to Result, because in most cases the Err case isn't quite as hot as the Ok case, so the wins on the latter can outweigh the losses from boxing on the former.

@thomcc
Copy link
Member

thomcc commented Jan 16, 2020

Also large_enum_variant doesn't fire on Result<T, E>, since (I think?) it's looking at the definition of the enum.

@Alexendoo Alexendoo linked a pull request Aug 30, 2022 that will close this issue
bors added a commit that referenced this issue Aug 30, 2022
Initial implementation `result_large_err`

This is a shot at #6560, #4652, and #3884. The lint checks for `Result` being returned from functions/methods where the `Err` variant is larger than a configurable threshold (the default of which is 128 bytes). There has been some discussion around this, which I'll try to quickly summarize:

* A large `Err`-variant may force an equally large `Result` if `Err` is actually bigger than `Ok`.
* There is a cost involved in large `Result`, as LLVM may choose to `memcpy` them around above a certain size.
* We usually expect the `Err` variant to be seldomly used, but pay the cost every time.
* `Result` returned from library code has a high chance of bubbling up the call stack, getting stuffed into `MyLibError { IoError(std::io::Error), ParseError(parselib::Error), ...}`, exacerbating the problem.

This PR deliberately does not take into account comparing the `Ok` to the `Err` variant (e.g. a ratio, or one being larger than the other). Rather we choose an absolute threshold for `Err`'s size, above which we warn. The reason for this is that `Err`s probably get `map_err`'ed further up the call stack, and we can't draw conclusions from the ratio at the point where the `Result` is returned. A relative threshold would also be less predictable, while not accounting for the cost of LLVM being forced to generate less efficient code if the `Err`-variant is _large_ in absolute terms.

We lint private functions as well as public functions, as the perf-cost applies to in-crate code as well.

In order to account for type-parameters, I conjured up `fn approx_ty_size`. The function relies on `LateContext::layout_of` to compute the actual size, and in case of failure (e.g. due to generics) tries to come up with an "at least size". In the latter case, the size of obviously wrong, but the inspected size certainly can't be smaller than that. Please give the approach a heavy dose of review, as I'm not actually familiar with the type-system at all (read: I have no idea what I'm doing).

The approach does, however flimsy it is, allow us to successfully lint situations like

```rust
pub union UnionError<T: Copy> {
    _maybe: T,
    _or_perhaps_even: (T, [u8; 512]),
}

// We know `UnionError<T>` will be at least 512 bytes, no matter what `T` is
pub fn param_large_union<T: Copy>() -> Result<(), UnionError<T>> {
    Ok(())
}
```

I've given some refactoring to `functions/result_unit_err.rs` to re-use some bits. This is also the groundwork for #6409

The default threshold is 128 because of #4652 (comment)

`lintcheck` does not trigger this lint for a threshold of 128. It does warn for 64, though.

The suggestion currently is the following, which is just a placeholder for discussion to be had. I did have the computed size in a `span_label`. However, that might cause both ui-tests here and lints elsewhere to become flaky wrt to their output (as the size is platform dependent).

```
error: the `Err`-variant returned via this `Result` is very large
  --> $DIR/result_large_err.rs:36:34
   |
LL | pub fn param_large_error<R>() -> Result<(), (u128, R, FullyDefinedLargeError)> {
   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The `Err` variant is unusually large, at least 128 bytes
```

changelog: Add [`result_large_err`] lint
bors added a commit that referenced this issue Apr 19, 2023
Add size-parameter to unecessary_box_returns

Fixes #10641

This adds a configuration-knob to the `unecessary_box_returns`-lint which allows _not_ linting a `fn() -> Box<T>` if `T` is "large". The default byte size above which we no longer lint is 128 bytes (due to #4652 (comment), also used in #9373). The overall rational is given in #10641.

---

changelog: Enhancement: [`unnecessary_box_returns`]: Added new lint configuration `unnecessary-box-size` to set the maximum size of `T` in `Box<T>` to be linted
[#10651](#10651)
<!-- changelog_checked -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-lint Area: New lints E-medium Call for participation: Medium difficulty level problem and requires some initial experience. L-perf Lint: Belongs in the perf lint group
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants