Skip to content

Commit 1b0f647

Browse files
committed
Rename to bench_black_box and add bench_input/output alternative
1 parent ce164ee commit 1b0f647

File tree

1 file changed

+69
-23
lines changed

1 file changed

+69
-23
lines changed

text/0000-black-box.md renamed to text/0000-bench-black-box.md

Lines changed: 69 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
- Feature Name: black_box
1+
- Feature Name: bench_black_box
22
- Start Date: 2018-03-12
33
- RFC PR: (leave this empty)
44
- Rust Issue: (leave this empty)
55

66
# Summary
77
[summary]: #summary
88

9-
This RFC adds `core::hint::black_box` (see [black box]), an identity function
9+
This RFC adds `core::hint::bench_black_box` (see [black box]), an identity function
1010
that hints the compiler to be maximally pessimistic in terms of the assumptions
11-
about what `black_box` could do.
11+
about what `bench_black_box` could do.
1212

13-
[black box]: https://en.wikipedia.org/wiki/Black_box
13+
[black box]: https://en.wikipedia.org/wiki/black_box
1414

1515
# Motivation
1616
[motivation]: #motivation
@@ -28,22 +28,22 @@ trying to model.
2828
# Guide-level explanation
2929
[guide-level-explanation]: #guide-level-explanation
3030

31-
## `hint::black_box`
31+
## `hint::bench_black_box`
3232

3333
The hint:
3434

3535
```rust
36-
pub fn black_box<T>(x: T) -> T;
36+
pub fn bench_black_box<T>(x: T) -> T;
3737
```
3838

3939
behaves like the [identity function][identity_fn]: it just returns `x` and has
4040
no effects. However, Rust implementations are _encouraged_ to assume that
41-
`black_box` can use `x` in any possible valid way that Rust code is allowed to
41+
`bench_black_box` can use `x` in any possible valid way that Rust code is allowed to
4242
without introducing undefined behavior in the calling code. That is,
4343
implementations are encouraged to be maximally pessimistic in terms of
4444
optimizations.
4545

46-
This property makes `black_box` useful for writing code in which certain
46+
This property makes `bench_black_box` useful for writing code in which certain
4747
optimizations are not desired, but too unreliable when disabling these
4848
optimizations is required for correctness.
4949

@@ -53,7 +53,7 @@ Example 1 ([`rust.godbolt.org`](https://godbolt.org/g/YP2GCJ)):
5353

5454
```rust
5555
fn foo(x: i32) -> i32 {
56-
hint::black_box(2 + x);
56+
hint::bench_black_box(2 + x);
5757
3
5858
}
5959
let a = foo(2);
@@ -62,12 +62,12 @@ let a = foo(2);
6262
In this example, the compiler may simplify the expression `2 + x` down to `4`.
6363
However, even though `4` is not read by anything afterwards, it must be computed
6464
and materialized, for example, by storing it into memory, a register, etc.
65-
because the current Rust implementation assumes that `black_box` could try to
65+
because the current Rust implementation assumes that `bench_black_box` could try to
6666
read it.
6767

6868
### Example 2 - benchmarking `Vec::push`
6969

70-
The `hint::black_box` is useful for producing synthetic benchmarks that more
70+
The `hint::bench_black_box` is useful for producing synthetic benchmarks that more
7171
accurately represent the behavior of a real application. In the following
7272
example, the function `bench` executes `Vec::push` 4 times in a loop:
7373

@@ -115,15 +115,15 @@ hint the compiler that the `Vec` is used for something
115115
```rust
116116
fn push_cap(v: &mut Vec<i32>) {
117117
for i in 0..4 {
118-
black_box(v.as_ptr());
119-
v.push(black_box(i));
120-
black_box(v.as_ptr());
118+
bench_black_box(v.as_ptr());
119+
v.push(bench_black_box(i));
120+
bench_black_box(v.as_ptr());
121121
}
122122
}
123123
```
124124

125125
Inspecting the machine code reveals that, for this particular Rust
126-
implementation, `black_box` successfully prevents LLVM from performing the
126+
implementation, `bench_black_box` successfully prevents LLVM from performing the
127127
optimization that removes the `Vec::push` calls that we wanted to measure.
128128

129129
# Reference-level explanation
@@ -134,18 +134,18 @@ The
134134
```rust
135135
mod core::hint {
136136
/// Identity function that disables optimizations.
137-
pub fn black_box<T>(x: T) -> T;
137+
pub fn bench_black_box<T>(x: T) -> T;
138138
}
139139
```
140140

141141
is a `NOP` that returns `x`, that is, its operational semantics are equivalent
142142
to the [identity function][identity_fn].
143143

144144

145-
Implementations are encouraged, _but not required_, to treat `black_box` as an
145+
Implementations are encouraged, _but not required_, to treat `bench_black_box` as an
146146
_unknown_ function that can perform any valid operation on `x` that Rust is
147147
allowed to perform without introducing undefined behavior in the calling code.
148-
That is, to optimize `black_box` under the pessimistic assumption that it might
148+
That is, to optimize `bench_black_box` under the pessimistic assumption that it might
149149
do anything with the data it got, even though it actually does nothing.
150150

151151
[identity_fn]: https://doc.rust-lang.org/nightly/std/convert/fn.identity.html
@@ -200,18 +200,55 @@ pub fn evaluate_and_drop<T>(x: T) {
200200
This approach is not pursued in this RFC because these two functions:
201201

202202
* add overhead ([`rust.godbolt.org`](https://godbolt.org/g/aCpPfg)): `volatile`
203-
reads and stores aren't no ops, but the proposed `black_box` and `clobber`
203+
reads and stores aren't no ops, but the proposed `bench_black_box` and `clobber`
204204
functions are.
205205
* are implementable on stable Rust: while we could add them to `std` they do not
206206
necessarily need to be there.
207207

208+
## `bench_input` / `bench_outpu`
209+
210+
@eddyb proposed
211+
[here](https://github.com/rust-lang/rfcs/pull/2360#issuecomment-463594450) (and
212+
the discussion that followed) to add two other hints instead:
213+
214+
* `bench_input`: `fn(T) -> T` (identity-like) may prevent some optimizations
215+
from seeing through the valid `T` value, more specifically, things like
216+
const/load-folding and range-analysis miri would still check the argument, and
217+
so it couldn't be e.g. uninitialized the argument computation can be
218+
optimized-out (unlike `bench_output`) mostly implementable today with the same
219+
strategy as `black_box`.
220+
221+
* `bench_output`: `fn(T) -> ()` (drop-like) may prevent some optimizations from
222+
optimizing out the computation of its argument the argument is not treated as
223+
"escaping into unknown code", i.e., you can't implement `bench_output(x)` as
224+
`{ bench_input(&mut x); x }`. What that would likely prevent is placing `x`
225+
into a register instead of memory, but optimizations might still see the old
226+
value of `x`, as if it couldn't have been mutated potentially implementable
227+
like `black_box` but `readonly`/`readnone` in LLVM.
228+
229+
From the RFC discussion there was consensus that we might want to add these
230+
benchmarking hints in the future as well because their are easier to specify and
231+
provide stronger guarantees than `bench_black_box`.
232+
233+
Right now, however, it is unclear whether these two hints can be implemented
234+
strictly in LLVM. The comment thread shows that the best we can actually do
235+
ends up implementing both of these as `bench_black_box` with the same effects.
236+
237+
Without a strict implementation, it is unclear which value these two intrinsics
238+
would add, and more importantly, since their difference in semantics cannot be
239+
shown, it is also unclear how we could teach users to use them correctly.
240+
241+
If we ever able to implement these correctly, we might want to consider
242+
deprecating `bench_black_box` at that point, but whether it will be worth
243+
deprecating is not clear either.
244+
208245
# Prior art
209246
[prior-art]: #prior-art
210247

211248
Similar functionality is provided in the [`Google
212249
Benchmark`](https://github.com/google/benchmark) C++ library: are called
213250
[`DoNotOptimize`](https://github.com/google/benchmark/blob/61497236ddc0d797a47ef612831fb6ab34dc5c9d/include/benchmark/benchmark.h#L306)
214-
(`black_box`) and
251+
(`bench_black_box`) and
215252
[`ClobberMemory`](https://github.com/google/benchmark/blob/61497236ddc0d797a47ef612831fb6ab34dc5c9d/include/benchmark/benchmark.h#L317).
216253
The `black_box` function with slightly different semantics is provided by the
217254
`test` crate:
@@ -220,12 +257,21 @@ The `black_box` function with slightly different semantics is provided by the
220257
# Unresolved questions
221258
[unresolved]: #unresolved-questions
222259

223-
* `const fn`: it is unclear whether `black_box` should be a `const fn`. If it
260+
* `const fn`: it is unclear whether `bench_black_box` should be a `const fn`. If it
224261
were, that would hint that it cannot have any side-effects, or that it cannot
225262
do anything that `const fn`s cannot do.
226263

227-
* Naming: it is unclear whether `black_box` is the right name for this primitive
228-
at this point. Some argumens in favor or against are that:
264+
* Naming: during the RFC discussion it was unclear whether `black_box` is the
265+
right name for this primitive but we settled on `bench_black_box` for the time
266+
being. We should resolve the naming before stabilization.
267+
268+
Also, we might want to add other benchmarking hints in the future, like
269+
`bench_input` and `bench_output`, so we might want to put all of this
270+
into a `bench` sub-module within the `core::hint` module. That might
271+
be a good place to explain how the benchmarking hints should be used
272+
holistically.
273+
274+
Some arguments in favor or against using "black box" are that:
229275
* pro: [black box] is a common term in computer programming, that conveys
230276
that nothing can be assumed about it except for its inputs and outputs.
231277
con: [black box] often hints that the function has no side-effects, but

0 commit comments

Comments
 (0)