pub fn black_box<T>(dummy: T) -> T
Expand description
一个标识函数,hints 编译器对 black_box
能做的事情保持最大限度的悲观。
与 std::convert::identity
不同,鼓励 Rust 编译器假定 black_box
可以以允许 Rust 代码使用的任何可能有效方式使用 dummy
,而不会在调用代码中引入未定义的行为。
此属性使 black_box
可用于编写不需要进行某些优化 (例如基准测试) 的代码。
但是请注意,black_box
仅 (并且只能) 以 “best-effort” 为基础提供。它可以阻止优化的程度可能会有所不同,具体取决于所使用的平台和代码源后端。
程序不能依赖 black_box
的正确性,除了它作为身份函数。
因此,**不得依赖它来控制关键程序行为。**这立即排除了将此函数直接用于加密或安全目的的任何可能性。
这什么时候有用?
虽然不适合那些关键任务的情况,但通常可以依赖 black_box 的功能进行基准测试,并且应该在那里使用。 它将尝试确保编译器不会根据上下文优化部分预期的测试代码。 例如:
fn contains(haystack: &[&str], needle: &str) -> bool {
haystack.iter().any(|x| x == &needle)
}
pub fn benchmark() {
let haystack = vec!["abc", "def", "ghi", "jkl", "mno"];
let needle = "ghi";
for _ in 0..10 {
contains(&haystack, needle);
}
}
Run编译器理论上可以进行如下优化:
needle
和haystack
一直一样,把调用移到循环外的contains
,删除循环- Inline
contains
needle
和haystack
的值在编译时已知,contains
始终为真。去掉调用,换成true
contains
的结果没有做任何事情: 完全删除这个函数调用benchmark
现在没有任何意义: 删除这个函数
上述所有情况不太可能发生,但编译器肯定能够进行一些优化,这可能会导致基准测试非常不准确。这就是 black_box
的用武之地:
use std::hint::black_box;
// 相同 `contains` 函数
fn contains(haystack: &[&str], needle: &str) -> bool {
haystack.iter().any(|x| x == &needle)
}
pub fn benchmark() {
let haystack = vec!["abc", "def", "ghi", "jkl", "mno"];
let needle = "ghi";
for _ in 0..10 {
// 调整我们的基准循环内容
black_box(contains(black_box(&haystack), black_box(needle)));
}
}
Run这实质上告诉编译器阻止对 black_box
的任何调用的优化。
所以,它现在:
- 将参数到
contains
都视为不可预测:contains
的主体不能再根据参数值进行优化 - 将调用
contains
及其结果视为易变的:benchmark
的主体无法优化它
这使我们的基准测试更现实地了解函数如何在原位使用,其中函数通常在编译时未知,结果以某种方式使用。