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

hint: Make spin_loop_hint a side-effect for all targets #77924

Closed
wants to merge 1 commit into from

Conversation

josephlr
Copy link
Contributor

@josephlr josephlr commented Oct 13, 2020

In rust-lang/rust-clippy#6161 and rust-lang/rust-clippy#6162 we were discussing changes to the empty_loop lint. That lint prohibits code like:

loop { }

for two reasons:

  • Hot dead-looping unnecessarily wastes CPU cycles
  • An LLVM codegen bug means that this code can cause UB

For std targets we recommend either panicking or replacing the code with:

loop {
  std::thread::sleep(...); // Could also be std::thread::yield_now();
}

For no_std targets, in an ideal world, we would just recommend panicking or replacing the code with:

loop {
  core::sync::atomic::spin_loop_hint();
}

While this would fix the performance issue (on some platforms), it wouldn't fix the LLVM codegen issue on all platforms. This change makes it so spin_loop_hint is always a side-effect from LLVM's perspective. This makes giving recommendations to users much easier. This change has no runtime costs (except that is disables optimizations we want disabled anyway). It also makes the behavior more consistent across targets (as spin_loop_hint is already a side-effect on x86 and (most) ARM targets).

Without this change, we would have to recommend very weird, unsafe things (like read_volatile). Furthermore, those recommendations would become stale once the LLVM codegen bug was fixed. With this change, we can recommend a fix that still makes sense once LLVM is fixed.

CC: @jonas-schievink @RalfJung @jyn514 @flip1995

Signed-off-by: Joe Richey [email protected]

This allows users to work around the LLVM codegen bug in stable Rust
by replacing
```rust
loop {}
```
with
```rust
loop {
  core::sync::atomic::spin_loop_hint();
}
```

Right now this workaround does the right thing on some (but not all) targets.

Signed-off-by: Joe Richey <[email protected]>
@rust-highfive
Copy link
Collaborator

r? @joshtriplett

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

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Oct 13, 2020
@jyn514 jyn514 added A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. labels Oct 13, 2020
@Mark-Simulacrum
Copy link
Member

I think I would rather we change codegen to explicitly emit an LLVM side effect on loop {}; that should be fairly easy to do, just no one has had the bandwidth to do so yet. I believe we had broad agreement that a partial fix would be good here even if we don't have a full fix available yet.

I don't think we should pessimize spin loop hint with inline asm volatile; that seems like it would potentially discourage it's use. Maybe it's not really important from llvm's perspective, though. Seems like we'd be hard pressed to tell since effects are likely invisible in most programs anyway.

@josephlr
Copy link
Contributor Author

Maybe it's not really important from llvm's perspective, though.

I wasn't able to get any sort of different codegen with this change. I would suspect that it probably does the exact same thing as marking the loop with llvm.sideeffect. I doubt that this would present an issue in practice (especially because it's already a side effect on the most common architectures).

I think I would rather we change codegen to explicitly emit an LLVM side effect on loop {}

This was my original idea, but after looking into it, it seemed hard to do in practice (but I don't have a lot of experience with the codegen part of rustc, so maybe it's not that hard).

@josephlr
Copy link
Contributor Author

I think I would rather we change codegen to explicitly emit an LLVM side effect on loop {}

Another thing that complicates this is that we don't want:

loop {
  core::sync::atomic::spin_loop_hint();
}

to be a regression w.r.t. the LLVM bug compared with loop { }. On non x86/ARM platfroms, spin_loop_hint contains no code, so maybe this is fine.

@RalfJung
Copy link
Member

RalfJung commented Oct 14, 2020

Seems like a reasonable hack to me, though I cannot comment on how much this would penalize other users of spin_loop_hint. I would think you'd usually in situations involving synchronization, where you have barriers or corresponding atomic instructions anyway and the compiler is already precluded from reordering things. But maybe this implementation prevents too much reordering, I wouldn't know.

Without this change, we would have to recommend very weird, unsafe things (like read_volatile). Furthermore, those recommendations would become stale once the LLVM codegen bug was fixed. With this change, we can recommend a fix that still makes sense once LLVM is fixed.

How does this still make any sense once LLVM is fixed? If you don't actually want a spinloop but just a program running forever, loop {} is fully correct then.

@josephlr
Copy link
Contributor Author

How does this still make any sense once LLVM is fixed? If you don't actually want a spinloop but just a program running forever, loop {} is fully correct then.

So the lint is actually two separate issues. First, a correctness issue: this LLVM bug. The second is a style/performance issue, where you usually don't want a "hot" deadloop, you want to do something in the loop to relax/sleep/pause the thread. For no_std, we can't sleep, but we can use this hint (which emits a reasonable instruction on x86 and ARM).

The linked clippy PR explains this in greater detail.

@josephlr
Copy link
Contributor Author

After reading more about PAUSE, https://software.intel.com/content/www/us/en/develop/articles/benefitting-power-and-performance-sleep-loops.html, we really shouldn't be recommending this as a way to reduce power in a dead-loop. On x86, PAUSE mostly effects the cache system by lowering the hyperthread's priority for cache requests. This is how it reduces power. I was unable to find any documentation that suggests dead-looping with a pause has any affect on power consumption (provided you're not waiting on some memory value to change).

Closing this. As @RalfJung mentioned, it doesn't make any sense once the LLVM bug is fixed.

@josephlr josephlr closed this Oct 15, 2020
@josephlr josephlr deleted the spin-loop branch November 15, 2020 05:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants