I've got two functions looping over a buffer, counting non-whitespace bytes. One of them uses an iterator, the other uses indexed access (unchecked!):
fn iter(buffer: &[u8], len: usize) -> usize {
let mut count = 0;
for &byte in &buffer[0..len] {
if !is_whitespace(byte) {
count += 1;
}
}
count
}
fn index_uncheck(buffer: &[u8], len: usize) -> usize {
let mut count = 0;
for index in 0..len {
unsafe {
if !is_whitespace(*buffer.get_unchecked(index)) {
count += 1;
}
}
}
count
}
In theory, they should have almost identical performance but in practice iter()
is ~13% faster on my test data. As @bluss on #rust channel suggested, the reason is index_uncheck()
doesn't get unrolled by an optimizer for some reason. And indeed, by looking into assembler output iter()
has these two similar looking chunks, while index_uncheck()
has only one.
.LBB0_7:
mov cl, byte ptr [r8]
add cl, -9
movzx edx, cl
cmp edx, 24
jae .LBB0_8
mov edx, 8388627
shr edx, cl
not edx
and edx, 1
add rax, rdx
jmp .LBB0_9
.align 16, 0x90
.LBB0_8:
inc rax
.LBB0_9:
mov cl, byte ptr [r8 + 1]
add cl, -9
movzx edx, cl
cmp edx, 24
jae .LBB0_10
mov edx, 8388627
shr edx, cl
not edx
and edx, 1
add rax, rdx
jmp .LBB0_13
.align 16, 0x90
.LBB0_10:
inc rax
And now for a really interesting twist
Here's the definition of is_whitespace()
:
#[inline(always)]
fn is_whitespace(value: u8) -> bool {
match value {
9 | 10 | 13 | 32 => true,
_ => false,
}
}
If I change it from using match
to OR-ed condition:
#[inline(always)]
fn is_whitespace(value: u8) -> bool {
value == 9 || value == 10 || value == 13 || value == 32
}
… then suddenly both types of looping get unrolled and start performing pretty much identically. Also, the assembler output looks drastically different, it's now full of SIMD instructions on XMM registers. It doesn't make any difference in final performance though…
Questions:
- Why is it happening? Some uncontrollable LLVM magic?
- Hypothetically speaking, where does one should start looking to fix it in the compiler? Or is MIR going to fix it automagically after ending world hunger and curing cancer?
P.S. Forgot to mention, sorry: it's on yesterday's nightly with cargo --release