1
- - Feature Name: black_box
1
+ - Feature Name: bench_black_box
2
2
- Start Date: 2018-03-12
3
3
- RFC PR: (leave this empty)
4
4
- Rust Issue: (leave this empty)
5
5
6
6
# Summary
7
7
[ summary ] : #summary
8
8
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
10
10
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.
12
12
13
- [ black box ] : https://en.wikipedia.org/wiki/Black_box
13
+ [ black box ] : https://en.wikipedia.org/wiki/black_box
14
14
15
15
# Motivation
16
16
[ motivation ] : #motivation
@@ -28,22 +28,22 @@ trying to model.
28
28
# Guide-level explanation
29
29
[ guide-level-explanation ] : #guide-level-explanation
30
30
31
- ## ` hint::black_box `
31
+ ## ` hint::bench_black_box `
32
32
33
33
The hint:
34
34
35
35
``` rust
36
- pub fn black_box <T >(x : T ) -> T ;
36
+ pub fn bench_black_box <T >(x : T ) -> T ;
37
37
```
38
38
39
39
behaves like the [ identity function] [ identity_fn ] : it just returns ` x ` and has
40
40
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
42
42
without introducing undefined behavior in the calling code. That is,
43
43
implementations are encouraged to be maximally pessimistic in terms of
44
44
optimizations.
45
45
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
47
47
optimizations are not desired, but too unreliable when disabling these
48
48
optimizations is required for correctness.
49
49
@@ -53,7 +53,7 @@ Example 1 ([`rust.godbolt.org`](https://godbolt.org/g/YP2GCJ)):
53
53
54
54
``` rust
55
55
fn foo (x : i32 ) -> i32 {
56
- hint :: black_box (2 + x );
56
+ hint :: bench_black_box (2 + x );
57
57
3
58
58
}
59
59
let a = foo (2 );
@@ -62,12 +62,12 @@ let a = foo(2);
62
62
In this example, the compiler may simplify the expression ` 2 + x ` down to ` 4 ` .
63
63
However, even though ` 4 ` is not read by anything afterwards, it must be computed
64
64
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
66
66
read it.
67
67
68
68
### Example 2 - benchmarking ` Vec::push `
69
69
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
71
71
accurately represent the behavior of a real application. In the following
72
72
example, the function ` bench ` executes ` Vec::push ` 4 times in a loop:
73
73
@@ -115,15 +115,15 @@ hint the compiler that the `Vec` is used for something
115
115
``` rust
116
116
fn push_cap (v : & mut Vec <i32 >) {
117
117
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 ());
121
121
}
122
122
}
123
123
```
124
124
125
125
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
127
127
optimization that removes the ` Vec::push ` calls that we wanted to measure.
128
128
129
129
# Reference-level explanation
@@ -134,18 +134,18 @@ The
134
134
``` rust
135
135
mod core :: hint {
136
136
/// Identity function that disables optimizations.
137
- pub fn black_box <T >(x : T ) -> T ;
137
+ pub fn bench_black_box <T >(x : T ) -> T ;
138
138
}
139
139
```
140
140
141
141
is a ` NOP ` that returns ` x ` , that is, its operational semantics are equivalent
142
142
to the [ identity function] [ identity_fn ] .
143
143
144
144
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
146
146
_ unknown_ function that can perform any valid operation on ` x ` that Rust is
147
147
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
149
149
do anything with the data it got, even though it actually does nothing.
150
150
151
151
[ 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) {
200
200
This approach is not pursued in this RFC because these two functions:
201
201
202
202
* 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 `
204
204
functions are.
205
205
* are implementable on stable Rust: while we could add them to ` std ` they do not
206
206
necessarily need to be there.
207
207
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
+
208
245
# Prior art
209
246
[ prior-art ] : #prior-art
210
247
211
248
Similar functionality is provided in the [ `Google
212
249
Benchmark`] ( https://github.com/google/benchmark ) C++ library: are called
213
250
[ ` DoNotOptimize ` ] ( https://github.com/google/benchmark/blob/61497236ddc0d797a47ef612831fb6ab34dc5c9d/include/benchmark/benchmark.h#L306 )
214
- (` black_box ` ) and
251
+ (` bench_black_box ` ) and
215
252
[ ` ClobberMemory ` ] ( https://github.com/google/benchmark/blob/61497236ddc0d797a47ef612831fb6ab34dc5c9d/include/benchmark/benchmark.h#L317 ) .
216
253
The ` black_box ` function with slightly different semantics is provided by the
217
254
` test ` crate:
@@ -220,12 +257,21 @@ The `black_box` function with slightly different semantics is provided by the
220
257
# Unresolved questions
221
258
[ unresolved ] : #unresolved-questions
222
259
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
224
261
were, that would hint that it cannot have any side-effects, or that it cannot
225
262
do anything that ` const fn ` s cannot do.
226
263
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:
229
275
* pro: [ black box] is a common term in computer programming, that conveys
230
276
that nothing can be assumed about it except for its inputs and outputs.
231
277
con: [ black box] often hints that the function has no side-effects, but
0 commit comments