@@ -219,6 +219,75 @@ pub fn spin_loop() {
219
219
/// backend used. Programs cannot rely on `black_box` for *correctness* in any way.
220
220
///
221
221
/// [`std::convert::identity`]: crate::convert::identity
222
+ ///
223
+ /// # When is this useful?
224
+ ///
225
+ /// First and foremost: `black_box` does _not_ guarantee any exact behavior and, in some cases, may
226
+ /// do nothing at all. As such, it **must not be relied upon to control critical program behavior.**
227
+ /// This _immediately_ precludes any direct use of this function for cryptographic or security
228
+ /// purposes.
229
+ ///
230
+ /// While not suitable in those mission-critical cases, `back_box`'s functionality can generally be
231
+ /// relied upon for benchmarking, and should be used there. It will try to ensure that the
232
+ /// compiler doesn't optimize away part of the intended test code based on context. For
233
+ /// example:
234
+ ///
235
+ /// ```
236
+ /// fn contains(haystack: &[&str], needle: &str) -> bool {
237
+ /// haystack.iter().any(|x| x == &needle)
238
+ /// }
239
+ ///
240
+ /// pub fn benchmark() {
241
+ /// let haystack = vec!["abc", "def", "ghi", "jkl", "mno"];
242
+ /// let needle = "ghi";
243
+ /// for _ in 0..10 {
244
+ /// contains(&haystack, needle);
245
+ /// }
246
+ /// }
247
+ /// ```
248
+ ///
249
+ /// The compiler could theoretically make optimizations like the following:
250
+ ///
251
+ /// - `needle` and `haystack` are always the same, move the call to `contains` outside the loop and
252
+ /// delete the loop
253
+ /// - Inline `contains`
254
+ /// - `needle` and `haystack` have values known at compile time, `contains` is always true. Remove
255
+ /// the call and replace with `true`
256
+ /// - Nothing is done with the result of `contains`: delete this function call entirely
257
+ /// - `benchmark` now has no purpose: delete this function
258
+ ///
259
+ /// It is not likely that all of the above happens, but the compiler is definitely able to make some
260
+ /// optimizations that could result in a very inaccurate benchmark. This is where `black_box` comes
261
+ /// in:
262
+ ///
263
+ /// ```
264
+ /// use std::hint::black_box;
265
+ ///
266
+ /// // Same `contains` function
267
+ /// fn contains(haystack: &[&str], needle: &str) -> bool {
268
+ /// haystack.iter().any(|x| x == &needle)
269
+ /// }
270
+ ///
271
+ /// pub fn benchmark() {
272
+ /// let haystack = vec!["abc", "def", "ghi", "jkl", "mno"];
273
+ /// let needle = "ghi";
274
+ /// for _ in 0..10 {
275
+ /// // Adjust our benchmark loop contents
276
+ /// black_box(contains(black_box(&haystack), black_box(needle)));
277
+ /// }
278
+ /// }
279
+ /// ```
280
+ ///
281
+ /// This essentially tells the compiler to block optimizations across any calls to `black_box`. So,
282
+ /// it now:
283
+ ///
284
+ /// - Treats both arguments to `contains` as unpredictable: the body of `contains` can no longer be
285
+ /// optimized based on argument values
286
+ /// - Treats the call to `contains` and its result as volatile: the body of `benchmark` cannot
287
+ /// optimize this away
288
+ ///
289
+ /// This makes our benchmark much more realistic to how the function would be used in situ, where
290
+ /// arguments are usually not known at compile time and the result is used in some way.
222
291
#[ inline]
223
292
#[ stable( feature = "bench_black_box" , since = "1.66.0" ) ]
224
293
#[ rustc_const_unstable( feature = "const_black_box" , issue = "none" ) ]
0 commit comments