-
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
Tracking issue for std::hint::black_box
#64102
Comments
I believe the main implementation work remaining is to enhance the documentation of |
An idea I wish I had earlier (but we can still try it out):
Note that because this is a warning, and not an error, it doesn't add to the dreaded post-monomorphization errors. We could also just do 2. but not 1, if we need to.
Would be interesting to know what the benchmarking frameworks out there use, in terms of test runners, and if they end up in a |
The term "fence" may also be appropriate, as it doesn't prevent optimisation of the value inside it, but rather, doesn't allow optimisations to cross the barrier. For example, |
Another argument in favor of |
I don't want to retreat ground that has been covered to exhaustion and beyond in the RFC discussion, but since all discussion here including the issue text is not even mentioning the key point that was blocking the
The semantics this concern was in reference to is (roughly) what's in the accepted RFC: this function is not actually guaranteed to be a "black box" to anyone, least of all the optimizer -- more explicitly: it is allowed to be implemented as completely transparent identity function, and behaves as such for purposes of e.g. whether a program is UB. Assuming nobody wants to re-litigate these semantics (doesn't seem like it so far, thank gods), anyone who wants the By the way, the same concern probably applies to most names mentioned as alternatives so far, as they all stress the "blocks optimizations / blinds the compiler" angle without simultaneously highlighting that it's is only suitable for benchmarks, where surprisingly aggressive optimizations only lead to confusing results rather than UB or other serious wrongness. |
We can implement this using the approach that kind of got consensus in #64683: fn bench_black_box_rt<T>(x: T) -> T { asm!("....") }
const fn bench_black_box_ct<T>(x: T) -> T { x }
pub const fn bench_black_box<T>(x: T) {
const_eval_select(bench_black_box_ct, bench_black_box_rt)
} |
(Hello. I ended up reading this issue because I tried to solve the problem
Sorry to pick up on this. I definitely do not want to re-litigate a decision which has already been made. However: I skimread a lot of these discussions including https://github.com/rust-lang/rust-memory-model/issues/45 . Much of the compiler internals went over my head; and as a result I wasn't able to see why this had been decided this way. Of course I looked in the RFC which ought to mention this under Alternatives, but it doesn't seem to discuss it at all. It seems to me that regardless of whether the semantics question is regarded as settled, it needs to be addressed in the RFC text. Especially if it was controversial! I.e. the Alternatives should have a section explaining why the proposal does not provide a useful semantic guarantee. The best description of the most desirable guarantee seems to me to be in rust-lang/rfcs#1484 (comment) or https://github.com/rust-lang/rust-memory-model/issues/45#issuecomment-374369870. Therefore it would probably be best for the RFC to quote one of those, or to paraphrase them, acknowledge that that would be the most useful thing to provide, and then explain why the RFC proposes something different. Since the existing RFC was merged, maybe this would need to be done as part of an attempt at stabilisation. With the current very weak semantics, I strongly agree with @hanna-kruppe's concerns over the misleading name. |
I also agree with @hanna-kruppe that
I particularly like fn push_cap(v: &mut Vec<i32>) {
for i in 0..4 {
assume_used(v.as_ptr());
v.push(bench_black_box(i));
assume_used(v.as_ptr());
}
} It also works even when used as an identity function: let x = assume_used(x); Is still fairly clear doesn't change the value in any way. |
I went ahead and created a PR that renames the method to |
@jonhoo I wonder how would it go along with other assume-like functions in |
@tmiasko I replied to your comment over in #74853 (comment). Figured it'd be good to keep all the discussion about this particular name suggestion to one PR. |
The current name is a legacy from before RFC 2360. The RFC calls the method `bench_black_box`, though acknowledges that this name is not be ideal either. The main objection to the name during and since the RFC is that it is not truly a black box to the compiler. Instead, the hint only encourages the compiler to consider the passed-in value used. This in the hope that the compiler will materialize that value somewhere, such as in memory or in a register, and not eliminate the input as dead code. This PR proposes to rename the method to `pretend_used`. This clearly indicates the precise semantics the hint conveys to the compiler, without suggesting that it disables further compiler optimizations. The name also reads straightforwardly in code: `hint::pretend_used(x)` hints to the compiler that it should pretend that `x` is used in some arbitrary way. `pretend_used` also rectifies the secondary naming concern the RFC raised with the names `black_box` and `bench_black_box`; the potential confusion with "boxed" values. If this change lands, it completes the naming portion of rust-lang#64102.
I was seeing some weird effects from using So I tried making my own, and ended up with this, which is sadly limited to values that fit in a register (godbolt): pub fn better_black_box(mut x: u64) -> u64 {
unsafe { asm!("/* {x} */", x = inout(reg) x); }
x
} From my brief testing, it seems to result in 3 less instructions than Might be interesting to see if we could specialize the version in libstd (based on the type) so that it's cheaper, for at least integers, but maybe there's not really any point to that. cc @Amanieu |
You don't even need specialization, you can just check |
You need to avoid the case when |
Adding asm!("/* {x} */", x = inout(reg) int_x, options(nostack)); example::better_black_box:
mov rax, rdi
#APP
#NO_APP
ret |
My latest suggestion from #74853 was |
This hasn't seen some activity in a while. Has an acceptable default implementation for this been found? It looks like the latest "best implementation" would be a combination of the proposals by @eddyb, @Amanieu, and @m-ou-se I made some modifications and created the following godbolt: fn better_black_box<T>(mut x: T) -> T {
unsafe {
if mem::size_of::<T>() <= mem::size_of::<usize>() {
let man_x = mem::ManuallyDrop::new(x);
let mut int_x: usize = mem::transmute_copy(&man_x);
asm!("/* {x} */", x = inout(reg) int_x, options(nostack));
x = mem::transmute_copy(&int_x);
} else {
asm!("/* {x} */", x = in(reg) &mut x);
}
}
x
} It's a small modification from their code that makes it work for anything that'll fit in a Has any consensus been come to on the name? If not, I'd like to humbly propose my own option: |
let mut int_x: usize = mem::transmute_copy(&man_x); This line is unsound if |
Team member @m-ou-se has proposed to merge this. The next step is review by the rest of the tagged team members: No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
I've nominated this issue for lang team discussion. |
If this is already something that can be done with inline asm, as demonstrated above, then it's "just" a small helper function in the standard library based on an existing stable part of the language. |
Every inline asm block is a language extension, unless its effect on the Rust-visible state can be achieved without using inline asm. Now in this case the effect on the Rust-visible change is a NOP, so it'd probably fine, but inline asm is sufficiently subtle and this a sufficiently high-profile feature that I feel it should be at least on their radar. |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
We discussed this in the @rust-lang/lang meeting today, and agreed that from a lang perspective this is fine to exist, because a conforming implementation can have it do nothing at all. (Thus it being in I would say it doing something useful is, like with |
The final comment period, with a disposition to merge, as per the review above, is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. This will be merged soon. |
…=TaKO8Ki Stabilize bench_black_box This PR stabilize `feature(bench_black_box)`. ```rust pub fn black_box<T>(dummy: T) -> T; ``` The FCP was completed in rust-lang#64102. `@rustbot` label +T-libs-api -T-libs
…=TaKO8Ki Stabilize bench_black_box This PR stabilize `feature(bench_black_box)`. ```rust pub fn black_box<T>(dummy: T) -> T; ``` The FCP was completed in rust-lang#64102. ``@rustbot`` label +T-libs-api -T-libs
…=TaKO8Ki Stabilize bench_black_box This PR stabilize `feature(bench_black_box)`. ```rust pub fn black_box<T>(dummy: T) -> T; ``` The FCP was completed in rust-lang#64102. `@rustbot` label +T-libs-api -T-libs
Stabilize bench_black_box This PR stabilize `feature(bench_black_box)`. ```rust pub fn black_box<T>(dummy: T) -> T; ``` The FCP was completed in rust-lang/rust#64102. `@rustbot` label +T-libs-api -T-libs
Stabilize bench_black_box This PR stabilize `feature(bench_black_box)`. ```rust pub fn black_box<T>(dummy: T) -> T; ``` The FCP was completed in rust-lang/rust#64102. `@rustbot` label +T-libs-api -T-libs
Closing this issue as it has been stabilised and wasn't tagged in the pr so it didn't get closed on merge |
Stabilize bench_black_box This PR stabilize `feature(bench_black_box)`. ```rust pub fn black_box<T>(dummy: T) -> T; ``` The FCP was completed in rust-lang/rust#64102. `@rustbot` label +T-libs-api -T-libs
This is a tracking issue for the RFC
std::hint:_black_box
.Original RFC: RFC 2360
Public API:
Steps:
Unresolved questions:
const fn
: it is unclear whetherbench_black_box
should be aconst fn
. If itwere, that would hint that it cannot have any side-effects, or that it cannot
do anything that
const fn
s cannot do.Naming: during the RFC discussion it was unclear whether
black_box
is theright name for this primitive but we settled on
bench_black_box
for the timebeing. We should resolve the naming before stabilization.
Also, we might want to add other benchmarking hints in the future, like
bench_input
andbench_output
, so we might want to put all of thisinto a
bench
sub-module within thecore::hint
module. That mightbe a good place to explain how the benchmarking hints should be used
holistically.
Some arguments in favor or against using "black box" are that:
that nothing can be assumed about it except for its inputs and outputs.
con: [black box] often hints that the function has no side-effects, but
this is not something that can be assumed about this API.
_box
has nothing to do withBox
orbox
-syntax, which might be confusingAlternative names suggested:
pessimize
,unoptimize
,unprocessed
,unknown
,do_not_optimize
(Google Benchmark).The text was updated successfully, but these errors were encountered: