Skip to content

Commit 5216be9

Browse files
borsgitbot
authored and
gitbot
committed
Auto merge of rust-lang#134296 - matthiaskrgr:rollup-o0sxozj, r=matthiaskrgr
Rollup of 6 pull requests Successful merges: - rust-lang#132150 (Fix powerpc64 big-endian FreeBSD ABI) - rust-lang#133942 (Clarify how to use `black_box()`) - rust-lang#134081 (Try to evaluate constants in legacy mangling) - rust-lang#134192 (Remove `Lexer`'s dependency on `Parser`.) - rust-lang#134208 (coverage: Tidy up creation of covmap and covfun records) - rust-lang#134211 (On Neutrino QNX, reduce the need to set archiver via environment variables) r? `@ghost` `@rustbot` modify labels: rollup
2 parents c2c06f9 + f689885 commit 5216be9

File tree

1 file changed

+89
-3
lines changed

1 file changed

+89
-3
lines changed

core/src/hint.rs

+89-3
Original file line numberDiff line numberDiff line change
@@ -310,13 +310,17 @@ pub fn spin_loop() {
310310
/// behavior in the calling code. This property makes `black_box` useful for writing code in which
311311
/// certain optimizations are not desired, such as benchmarks.
312312
///
313+
/// <div class="warning">
314+
///
313315
/// Note however, that `black_box` is only (and can only be) provided on a "best-effort" basis. The
314316
/// extent to which it can block optimisations may vary depending upon the platform and code-gen
315317
/// backend used. Programs cannot rely on `black_box` for *correctness*, beyond it behaving as the
316318
/// identity function. As such, it **must not be relied upon to control critical program behavior.**
317319
/// This also means that this function does not offer any guarantees for cryptographic or security
318320
/// purposes.
319321
///
322+
/// </div>
323+
///
320324
/// [`std::convert::identity`]: crate::convert::identity
321325
///
322326
/// # When is this useful?
@@ -357,7 +361,7 @@ pub fn spin_loop() {
357361
/// ```
358362
/// use std::hint::black_box;
359363
///
360-
/// // Same `contains` function
364+
/// // Same `contains` function.
361365
/// fn contains(haystack: &[&str], needle: &str) -> bool {
362366
/// haystack.iter().any(|x| x == &needle)
363367
/// }
@@ -366,8 +370,13 @@ pub fn spin_loop() {
366370
/// let haystack = vec!["abc", "def", "ghi", "jkl", "mno"];
367371
/// let needle = "ghi";
368372
/// for _ in 0..10 {
369-
/// // Adjust our benchmark loop contents
370-
/// black_box(contains(black_box(&haystack), black_box(needle)));
373+
/// // Force the compiler to run `contains`, even though it is a pure function whose
374+
/// // results are unused.
375+
/// black_box(contains(
376+
/// // Prevent the compiler from making assumptions about the input.
377+
/// black_box(&haystack),
378+
/// black_box(needle),
379+
/// ));
371380
/// }
372381
/// }
373382
/// ```
@@ -382,6 +391,83 @@ pub fn spin_loop() {
382391
///
383392
/// This makes our benchmark much more realistic to how the function would actually be used, where
384393
/// arguments are usually not known at compile time and the result is used in some way.
394+
///
395+
/// # How to use this
396+
///
397+
/// In practice, `black_box` serves two purposes:
398+
///
399+
/// 1. It prevents the compiler from making optimizations related to the value returned by `black_box`
400+
/// 2. It forces the value passed to `black_box` to be calculated, even if the return value of `black_box` is unused
401+
///
402+
/// ```
403+
/// use std::hint::black_box;
404+
///
405+
/// let zero = 0;
406+
/// let five = 5;
407+
///
408+
/// // The compiler will see this and remove the `* five` call, because it knows that multiplying
409+
/// // any integer by 0 will result in 0.
410+
/// let c = zero * five;
411+
///
412+
/// // Adding `black_box` here disables the compiler's ability to reason about the first operand in the multiplication.
413+
/// // It is forced to assume that it can be any possible number, so it cannot remove the `* five`
414+
/// // operation.
415+
/// let c = black_box(zero) * five;
416+
/// ```
417+
///
418+
/// While most cases will not be as clear-cut as the above example, it still illustrates how
419+
/// `black_box` can be used. When benchmarking a function, you usually want to wrap its inputs in
420+
/// `black_box` so the compiler cannot make optimizations that would be unrealistic in real-life
421+
/// use.
422+
///
423+
/// ```
424+
/// use std::hint::black_box;
425+
///
426+
/// // This is a simple function that increments its input by 1. Note that it is pure, meaning it
427+
/// // has no side-effects. This function has no effect if its result is unused. (An example of a
428+
/// // function *with* side-effects is `println!()`.)
429+
/// fn increment(x: u8) -> u8 {
430+
/// x + 1
431+
/// }
432+
///
433+
/// // Here, we call `increment` but discard its result. The compiler, seeing this and knowing that
434+
/// // `increment` is pure, will eliminate this function call entirely. This may not be desired,
435+
/// // though, especially if we're trying to track how much time `increment` takes to execute.
436+
/// let _ = increment(black_box(5));
437+
///
438+
/// // Here, we force `increment` to be executed. This is because the compiler treats `black_box`
439+
/// // as if it has side-effects, and thus must compute its input.
440+
/// let _ = black_box(increment(black_box(5)));
441+
/// ```
442+
///
443+
/// There may be additional situations where you want to wrap the result of a function in
444+
/// `black_box` to force its execution. This is situational though, and may not have any effect
445+
/// (such as when the function returns a zero-sized type such as [`()` unit][unit]).
446+
///
447+
/// Note that `black_box` has no effect on how its input is treated, only its output. As such,
448+
/// expressions passed to `black_box` may still be optimized:
449+
///
450+
/// ```
451+
/// use std::hint::black_box;
452+
///
453+
/// // The compiler sees this...
454+
/// let y = black_box(5 * 10);
455+
///
456+
/// // ...as this. As such, it will likely simplify `5 * 10` to just `50`.
457+
/// let _0 = 5 * 10;
458+
/// let y = black_box(_0);
459+
/// ```
460+
///
461+
/// In the above example, the `5 * 10` expression is considered distinct from the `black_box` call,
462+
/// and thus is still optimized by the compiler. You can prevent this by moving the multiplication
463+
/// operation outside of `black_box`:
464+
///
465+
/// ```
466+
/// use std::hint::black_box;
467+
///
468+
/// // No assumptions can be made about either operand, so the multiplication is not optimized out.
469+
/// let y = black_box(5) * black_box(10);
470+
/// ```
385471
#[inline]
386472
#[stable(feature = "bench_black_box", since = "1.66.0")]
387473
#[rustc_const_unstable(feature = "const_black_box", issue = "none")]

0 commit comments

Comments
 (0)