Я смотрю на производительность memchr
-подобных функций и сделал интересное наблюдение.
Это check.c
с тремя реализациями, чтобы найти смещение символа \n
в строке:
#include <stdlib.h>
size_t mem1(const char *s)
{
const char *p = s;
while (1)
{
const char x = *p;
if (x == '\n') return (p - s);
p++;
}
}
size_t mem2(const char *s)
{
const char *p = s;
while (1)
{
const char x = *p;
if (x <= '$' && (x == '\n' || x == '\0')) return (p - s);
p++;
}
}
size_t mem3(const char *s)
{
const char *p = s;
while (1)
{
const char x = *p;
if (x == '\n' || x == '\0') return (p - s);
p++;
}
}
size_t mem4(const char *s)
{
const char *p = s;
while (1)
{
const char x = *p;
if (x <= '$' && (x == '\n')) return (p - s);
p++;
}
}
Я запускаю эти функции в строке байтов, которая может быть описана выражением Haskell (concat $ replicate 10000 "abcd") ++ "\n" ++ "hello"
- это 10000 раз asdf
, затем новая строка для поиска, а затем hello
. Конечно, все 3 реализации возвращают одинаковое смещение: 40000, как ожидалось.
Интересно, что при компиляции с gcc -O2
время выполнения этой строки:
-
mem1
: 16 us -
mem2
: 12 us -
mem3
: 25 us -
mem4
: 16 us
(Я использую библиотеку criterion для измерения этих времен с статистической точностью.)
Я не могу объяснить это самому себе. Почему mem2
намного быстрее, чем два других?
-
Сборка, созданная с помощью gcc -S -O2 -o check.asm check.c
:
mem1:
.LFB14:
cmpb $10, (%rdi)
movq %rdi, %rax
je .L9
.L6:
addq $1, %rax
cmpb $10, (%rax)
jne .L6
subq %rdi, %rax
ret
.L9:
xorl %eax, %eax
ret
mem2:
.LFB15:
movq %rdi, %rax
jmp .L13
.L19:
cmpb $10, %dl
je .L14
.L11:
addq $1, %rax
.L13:
movzbl (%rax), %edx
cmpb $36, %dl
jg .L11
testb %dl, %dl
jne .L19
.L14:
subq %rdi, %rax
ret
mem3:
.LFB16:
movzbl (%rdi), %edx
testb %dl, %dl
je .L26
cmpb $10, %dl
movq %rdi, %rax
jne .L27
jmp .L26
.L30:
cmpb $10, %dl
je .L23
.L27:
addq $1, %rax
movzbl (%rax), %edx
testb %dl, %dl
jne .L30
.L23:
subq %rdi, %rax
ret
.L26:
xorl %eax, %eax
ret
mem4:
.LFB17:
cmpb $10, (%rdi)
movq %rdi, %rax
je .L38
.L36:
addq $1, %rax
cmpb $10, (%rax)
jne .L36
subq %rdi, %rax
ret
.L38:
xorl %eax, %eax
ret
Любое объяснение очень ценится!