"match" compiles into suboptimal asm compared to "if/else"

Viewed 201

Consider the following binary search tree node:

pub struct Node {
    key: i64,
    left_ptr: Option<Box<Node>>,
    right_ptr: Option<Box<Node>>,
}

Here are two implementations of "contains" method with identical behavior:

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
}

However, the ASM code they translate into differs significantly:

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
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

Playground.

According to my benchmarks, the "match" version is 15 to 25% slower then then the "if/else" version:

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]

Benchmarks.

Is there a reason why the "match" version is compiled into a slower asm?

0 Answers
Related