Skip to content

Commit b6b374c

Browse files
committed
feat: address feedback and expand on examples
1 parent b65b5ef commit b6b374c

File tree

1 file changed

+79
-28
lines changed

1 file changed

+79
-28
lines changed

library/core/src/hint.rs

Lines changed: 79 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -310,34 +310,6 @@ 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-
/// In practice, `black_box` serves two purposes:
314-
///
315-
/// 1. It forces the input to be calculated, even if its results are never used
316-
/// 2. It prevents the compiler from making optimizations related to the value of the returned
317-
/// type
318-
///
319-
/// Note that `black_box` does not prevent its inputs from being optimized before they are passed
320-
/// to the function, though.
321-
///
322-
/// ```
323-
/// # use std::hint::black_box;
324-
/// #
325-
/// // This...
326-
/// let y = black_box(5 * 10);
327-
/// // ...will still be optimized to this:
328-
/// let y = black_box(50);
329-
/// ```
330-
///
331-
/// In the above example, `5 * 10` is replaced with `50` by the compiler. You can prevent this by
332-
/// moving the multiplication outside of `black_box`:
333-
///
334-
/// ```
335-
/// # use std::hint::black_box;
336-
/// #
337-
/// // No assumptions can be made about either number, so the multiplication is kept.
338-
/// let y = black_box(5) * black_box(10);
339-
/// ```
340-
///
341313
/// <div class="warning">
342314
///
343315
/// Note however, that `black_box` is only (and can only be) provided on a "best-effort" basis. The
@@ -419,6 +391,85 @@ pub fn spin_loop() {
419391
///
420392
/// This makes our benchmark much more realistic to how the function would actually be used, where
421393
/// 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 of the returned
400+
/// type
401+
/// 2. It forces the input to be calculated, even if its results are never used
402+
///
403+
/// ```
404+
/// use std::hint::black_box;
405+
///
406+
/// let zero = 0;
407+
/// let five = 5;
408+
///
409+
/// // The compiler will see this and remove the `* five` call, because it knows that multiplying
410+
/// // any integer by 0 will result in 0. This is a value optimization: the compiler knows the
411+
/// // value of `zero` must be 0, and thus can make optimizations related to that.
412+
/// let c = zero * five;
413+
///
414+
/// // Adding `black_box` here disables the compiler's ability to reason about the value of `zero`.
415+
/// // It is forced to assume that it can be any possible number, so it cannot remove the `* five`
416+
/// // operation.
417+
/// let c = black_box(zero) * five;
418+
/// ```
419+
///
420+
/// While most cases will not be as clear-cut as the above example, it still illustrates how
421+
/// `black_box` can be used. When benchmarking a function, you usually want to wrap its inputs in
422+
/// `black_box` so the compiler cannot make optimizations that would be unrealistic in real-life
423+
/// use.
424+
///
425+
/// ```
426+
/// use std::hint::black_box;
427+
///
428+
/// // This is a simple function that increments its input by 1. Note that it is pure, meaning it
429+
/// // has no side-effects. This function has no effect if its result is unused. (An example of a
430+
/// // function *with* side-effects is `println!()`.)
431+
/// fn increment(x: u8) -> u8 {
432+
/// x + 1
433+
/// }
434+
///
435+
/// // Here, we call `increment` but discard its result. The compiler, seeing this and knowing that
436+
/// // `increment` is pure, will eliminate this function call entirely. This may not be desired,
437+
/// // though, especially if we're trying to track how much time `increment` takes to execute.
438+
/// let _ = increment(black_box(5));
439+
///
440+
/// // Here, we force `increment` to be executed. This is because the compiler treats `black_box`
441+
/// // as if it has side-effects, and thus must compute its input.
442+
/// let _ = black_box(increment(black_box(5)));
443+
/// ```
444+
///
445+
/// There may be additional situations where you want to wrap the result of a function in
446+
/// `black_box` to force its execution. This is situational though, and may not have any effect
447+
/// (such as when the function returns a [`()` unit][unit]).
448+
///
449+
/// Note that `black_box` has no effect on how its input is treated, only its output. As such,
450+
/// expressions passed to `black_box` may still be optimized:
451+
///
452+
/// ```
453+
/// use std::hint::black_box;
454+
///
455+
/// // The compiler sees this...
456+
/// let y = black_box(5 * 10);
457+
///
458+
/// // ...as this. As such, it will likely simplify `5 * 10` to just `50`.
459+
/// let _0 = 5 * 10;
460+
/// let y = black_box(_0);
461+
/// ```
462+
///
463+
/// In the above example, the `5 * 10` expression is considered distinct from the `black_box` call,
464+
/// and thus is still optimized by the compiler. You can prevent this by moving the multiplication
465+
/// operation outside of `black_box`:
466+
///
467+
/// ```
468+
/// use std::hint::black_box;
469+
///
470+
/// // No assumptions can be made about either number, so the multiplication is kept.
471+
/// let y = black_box(5) * black_box(10);
472+
/// ```
422473
#[inline]
423474
#[stable(feature = "bench_black_box", since = "1.66.0")]
424475
#[rustc_const_unstable(feature = "const_black_box", issue = "none")]

0 commit comments

Comments
 (0)