Skip to content

Ch 8: std::hint - 编译器提示

std::hint 模块提供了一系列让开发者向编译器传递优化提示的函数。这些提示帮助编译器生成更高效的机器码,同时不影响程序的语义正确性。本章将讲解 likely、unlikely、assume、unreachable 和 inline 等函数。

2. likely 和 unlikely - 分支预测提示

Section titled “2. likely 和 unlikely - 分支预测提示”

这两个函数向编译器提示条件的预期结果,帮助 CPU 进行分支预测优化:

函数签名:

#[inline]
pub const fn likely(x: bool) -> bool { x }
#[inline]
pub const fn unlikely(x: bool) -> bool { x }

示例:

use std::hint;
fn main() {
let condition = false;
// 告诉编译器这个条件通常为 false
if hint::unlikely(condition) {
println!("unlikely branch");
}
// 告诉编译器这个条件通常为 true
if hint::likely(!condition) {
println!("likely branch");
}
}

性能影响:

use std::hint;
fn process_data(data: &[u8]) -> usize {
let mut sum = 0;
for byte in data {
// 通常情况下 byte 不为 0
if hint::likely(*byte != 0) {
sum += 1;
} else {
// 罕见的零字节情况
sum += 100;
}
}
sum
}
fn main() {
let data = vec![1, 2, 3, 0, 4, 5];
println!("result: {}", process_data(&data));
}

assume 告诉编译器某个条件保证为真,允许编译器进行更激进的优化:

函数签名:

#[inline]
#[rustc_const_strict(const_strict)]
pub const unsafe fn assume(b: bool) -> () { ... }

示例:

use std::hint;
fn divide_if_valid(a: i32, b: i32) -> Option<i32> {
unsafe {
// 告诉编译器 b 不为零
// 如果违反,会导致未定义行为!
std::hint::assume(b != 0);
}
Some(a / b)
}
fn main() {
println!("{:?}", divide_if_valid(10, 2)); // Some(5)
// 注意:如果传递 b=0,行为是未定义的!
}

使用场景 - 手动边界检查:

use std::hint;
use std::slice;
fn safe_first(slice_data: &[u32]) -> u32 {
// 假设 slice 不为空(调用者保证)
unsafe {
hint::assume(!slice_data.is_empty());
}
slice_data[0]
}
fn main() {
let data = vec![10, 20, 30];
println!("{}", safe_first(&data));
}

unreachable 标记一段代码永远不会被执行:

函数签名:

#[inline]
#[rustc_const_strict(const_strict)]
pub const unsafe fn unreachable() -> ! { ... }

示例:

use std::hint;
fn classify(n: i32) -> &'static str {
if n < 0 {
"negative"
} else if n == 0 {
"zero"
} else if n > 0 {
// 编译器知道所有情况都被处理了
// 但有时类型范围很大,需要明确标记
unsafe { std::hint::unreachable_unchecked() }
} else {
// 永远不会执行到这里
unsafe { std::hint::unreachable() }
}
}
fn main() {
println!("{}", classify(5)); // positive
println!("{}", classify(0)); // zero
println!("{}", classify(-3)); // negative
}

unreachable_unchecked - 无界版本:

use std::hint;
fn get_unchecked<T>(slice: &[T], index: usize) -> &T {
if index < slice.len() {
unsafe {
// 如果 index 超出范围,后果自负
return std::hint::unreachable_unchecked();
}
}
&slice[index]
}

inline 属性提示编译器将函数内联:

属性形式:

#[inline]
fn inlined_function() {}
#[inline(always)]
fn always_inlined() {}
#[inline(never)]
fn never_inlined() {}

函数形式:

use std::hint;
fn call_me() {
// 提示编译器考虑内联
hint::inline_always(inlined);
// 提示编译器不要内联
hint::inline_never(not_inlined);
}
#[inline(always)]
fn inlined() {
println!("inlined");
}
#[inline(never)]
fn not_inlined() {
println!("not inlined");
}

spin_loop 提示 CPU 当前处于忙等待状态:

use std::hint::spin_loop;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
fn wait_for_flag(flag: &AtomicBool) {
while !flag.load(Ordering::SeqCst) {
// 提示 CPU 我们在忙等待,让它优化
spin_loop();
// 更好的方式是使用适当的同步原语
}
}
fn main() {
let flag = AtomicBool::new(false);
thread::spawn({
let flag = &flag;
move || {
thread::sleep(std::time::Duration::from_millis(100));
flag.store(true, Ordering::SeqCst);
}
});
wait_for_flag(&flag);
println!("Flag is true!");
}

black_box 阻止编译器优化掉变量:

use std::hint::black_box;
fn main() {
let x = 42;
// 编译器可能优化掉这个计算
// black_box 告诉编译器"使用"这个值,防止优化
let y = black_box(x);
println!("{}", y);
}
use std::hint;
use std::str::FromStr;
struct Parser {
input: Vec<u8>,
position: usize,
}
impl Parser {
fn new(input: Vec<u8>) -> Self {
Parser { input, position: 0 }
}
fn parse_u64(&mut self) -> Option<u64> {
// 快速路径:检查是否有足够数据
if hint::likely(self.position < self.input.len()) {
// 解析数字...
let start = self.position;
while self.position < self.input.len()
&& self.input[self.position].is_ascii_digit()
{
self.position += 1;
}
if self.position > start {
let s = std::str::from_utf8(&self.input[start..self.position]).ok()?;
return u64::from_str(s).ok();
}
}
None
}
}
fn main() {
let data = b"42 100 200".to_vec();
let mut parser = Parser::new(data);
println!("{:?}", parser.parse_u64()); // Some(42)
println!("{:?}", parser.parse_u64()); // Some(100)
println!("{:?}", parser.parse_u64()); // Some(200)
}
  1. assume 是 unsafe 的:如果假设违反,会导致未定义行为,必须确保条件确实为真。

  2. unreachable 也是 unsafe 的:标记为不可达的代码如果被执行,后果未定义。

  3. likely/unlikely 是 hints:编译器可以忽略这些提示,但通常会尊重。

  4. inline 属性 vs 函数:两种方式效果相同,inline 属性更常用。

  5. black_box 可能不起作用:编译器优化能力很强,某些情况下可能仍然优化掉。

  6. 过度使用的风险:滥用 hint 可能导致代码可读性下降,或在硬件变化后适得其反。

std::hint 模块提供了编译器优化提示:

函数作用安全性
likely(b)提示条件通常为真安全
unlikely(b)提示条件通常为假安全
assume(b)断言条件为真unsafe
unreachable()标记代码不可达unsafe
unreachable_unchecked()无界不可达标记unsafe
inline_always强制内联安全
inline_never阻止内联安全
spin_loop忙等待提示安全
black_box防止优化安全

这些 hint 在性能关键代码中很有价值,但应该谨慎使用,确保不会引入未定义行为。