我编写了这个非常简单的 Rust 函数:
fn iterate(nums: &Box<[i32]>) -> i32 {
let mut total = 0;
let len = nums.len();
for i in 0..len {
if nums[i] > 0 {
total += nums[i];
} else {
total -= nums[i];
}
}
total
}
我已经写了一个基本的基准测试,用有序数组和乱序数组调用该方法:
fn criterion_benchmark(c: &mut Criterion) {
const SIZE: i32 = 1024 * 1024;
let mut group = c.benchmark_group("Branch Prediction");
// setup benchmarking for an ordered array
let mut ordered_nums: Vec<i32> = vec![];
for i in 0..SIZE {
ordered_nums.push(i - SIZE/2);
}
let ordered_nums = ordered_nums.into_boxed_slice();
group.bench_function("ordered", |b| b.iter(|| iterate(&ordered_nums)));
// setup benchmarking for a shuffled array
let mut shuffled_nums: Vec<i32> = vec![];
for i in 0..SIZE {
shuffled_nums.push(i - SIZE/2);
}
let mut rng = thread_rng();
let mut shuffled_nums = shuffled_nums.into_boxed_slice();
shuffled_nums.shuffle(&mut rng);
group.bench_function("shuffled", |b| b.iter(|| iterate(&shuffled_nums)));
group.finish();
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
我很惊讶这两个基准测试的运行时间几乎一样,而在Java中类似的基准测试在两种情况下表现出截然不同,可能是由于洗牌案例中分支预测失败所致。
我看到有提到条件移动指令,但如果我在可执行文件上运行 otool -tv
(我正在Mac上运行),我在iterate
方法输出中没有看到任何指令。
是否有人能够说明为什么Rust中有序和无序情况之间没有明显的性能差异呢?
cmovll
来展开逻辑。 - sshashank124-O3
)后,像LLVM和GCC这样的现代预编译编译器后端通常会将分支转换为CMOV或其他无分支序列。这也是自动向量化的先决条件。 - Peter Cordes