考虑以下二叉搜索树节点:
pub struct Node {
key: i64,
left_ptr: Option<Box<Node>>,
right_ptr: Option<Box<Node>>,
}
Run Code Online (Sandbox Code Playgroud)
以下是具有相同行为的“包含”方法的两种实现:
pub fn contains_match(root: &Option<Box<Node>>, key: i64) -> bool {
let mut node_ptr = root;
while let Some(node) = node_ptr {
match node.key.cmp(&key) {
Ordering::Equal => return true,
Ordering::Less => node_ptr = &node.right_ptr,
Ordering::Greater => node_ptr = &node.left_ptr,
}
}
false
}
pub fn contains_if_else(root: &Option<Box<Node>>, key: i64) -> bool {
let mut node_ptr = root;
while let Some(node) = node_ptr {
if node.key == key {
return true;
}
if node.key < key {
node_ptr = &node.right_ptr;
} else {
node_ptr = &node.left_ptr;
}
}
false
}
Run Code Online (Sandbox Code Playgroud)
但是,它们转换成的 ASM 代码有很大不同:
playground::contains_match:
movq (%rdi), %rax
testq %rax, %rax
je .LBB0_7
movq $-1, %rcx
.LBB0_2:
xorl %edx, %edx
cmpq %rsi, (%rax)
setne %dl
cmovlq %rcx, %rdx
cmpq $-1, %rdx
je .LBB0_5
movb $1, %al
testq %rdx, %rdx
je .LBB0_8
movq (%rdi), %rdi
addq $8, %rdi
movq (%rdi), %rax
testq %rax, %rax
jne .LBB0_2
jmp .LBB0_7
.LBB0_5:
movq (%rdi), %rdi
addq $16, %rdi
movq (%rdi), %rax
testq %rax, %rax
jne .LBB0_2
.LBB0_7:
xorl %eax, %eax
.LBB0_8:
retq
Run Code Online (Sandbox Code Playgroud)
playground::contains_if_else:
cmpq $0, (%rdi)
je .LBB1_5
.LBB1_2:
movq (%rdi), %rdi
movq (%rdi), %rax
cmpq %rsi, %rax
je .LBB1_3
leaq 16(%rdi), %rcx
addq $8, %rdi
cmpq %rsi, %rax
cmovlq %rcx, %rdi
cmpq $0, (%rdi)
jne .LBB1_2
.LBB1_5:
xorl %eax, %eax
retq
.LBB1_3:
movb $1, %al
retq
Run Code Online (Sandbox Code Playgroud)
根据我的基准测试,“match”版本比“if/else”版本慢 15% 到 25%:
100k_random_lookup_hits/contains_if_else
time: [14.096 ms 14.146 ms 14.212 ms]
100k_random_lookup_hits/contains_match
time: [17.938 ms 17.961 ms 17.991 ms]
100k_random_lookup_misses/contains_if_else
time: [20.451 ms 20.475 ms 20.502 ms]
100k_random_lookup_misses/contains_match
time: [23.900 ms 23.920 ms 23.943 ms]
Run Code Online (Sandbox Code Playgroud)
“匹配”版本被编译成较慢的汇编是否有原因?
小智 1
所以这将是 LLVM 的问题,而不是 Rust 特有的问题,因为似乎出于某种原因 switch 表达式没有得到正确的优化。
switch i8 %_6.0, label %bb4 [
i8 -1, label %bb6
i8 0, label %bb9
i8 1, label %bb3
], !dbg !22
Run Code Online (Sandbox Code Playgroud)
我在 Godbolt 上玩了很长时间。无法弄清楚为什么,但这似乎已经成为一个问题很长一段时间了。据我所知,在大多数情况下匹配仍然更好,但这种情况确实会发生,特别是对于像这样的少量分支。
https://llvm.org/pubs/2007-05-31-Switch-Lowering.html
https://llvm.org/devmtg/2015-10/slides/Wennborg-SwitchLowering.pdf