memcmp经常在汇编中实现,以利用许多体系结构特定的功能,这使得它比在C语言中使用简单循环要快得多。
作为“内置”函数
GCC支持memcmp(以及大量其他函数)作为内置函数。在某些版本/配置的GCC中,对memcmp的调用将被识别为__builtin_memcmp。GCC不会发出对memcmp库函数的调用,而是发出一些指令来充当优化的内联版本。
在x86上,这利用了cmpsb指令,该指令将一个内存位置处的字节字符串与另一个进行比较。这与repe前缀配合使用,因此字符串会被比较直到它们不再相等或计数用尽。 (正是memcmp所做的)。
给定以下代码:
int test(const void* s1, const void* s2, int count)
{
return memcmp(s1, s2, count) == 0;
}
gcc version 3.4.4在Cygwin上生成以下汇编代码:
; (prologue)
mov esi, [ebp+arg_0] ; Move first pointer to esi
mov edi, [ebp+arg_4] ; Move second pointer to edi
mov ecx, [ebp+arg_8] ; Move length to ecx
cld ; Clear DF, the direction flag, so comparisons happen
; at increasing addresses
cmp ecx, ecx ; Special case: If length parameter to memcmp is
; zero, don't compare any bytes.
repe cmpsb ; Compare bytes at DS:ESI and ES:EDI, setting flags
; Repeat this while equal ZF is set
setz al ; Set al (return value) to 1 if ZF is still set
; (all bytes were equal).
; (epilogue)
参考资料:
cmpsb 指令
作为库函数
许多 C 标准库都有高度优化的 memcmp 版本。这些版本通常利用特定于架构的指令来并行处理大量数据。
在 Glibc 中,有适用于以下指令集扩展的 memcmp 版本 x86_64:
SSE2 - sysdeps/x86_64/memcmp.S
SSE4 - sysdeps/x86_64/multiarch/memcmp-sse4.S
SSSE3 - sysdeps/x86_64/multiarch/memcmp-ssse3.S
很酷的是,glibc 会在运行时检测您的 CPU 拥有的最新指令集,并执行针对该指令集进行优化的版本。请参见来自sysdeps/x86_64/multiarch/memcmp.S的以下代码片段:
ENTRY(memcmp)
.type memcmp, @gnu_indirect_function
LOAD_RTLD_GLOBAL_RO_RDX
HAS_CPU_FEATURE (SSSE3)
jnz 2f
leaq __memcmp_sse2(%rip), %rax
ret
2: HAS_CPU_FEATURE (SSE4_1)
jz 3f
leaq __memcmp_sse4_1(%rip), %rax
ret
3: leaq __memcmp_ssse3(%rip), %rax
ret
END(memcmp)
在Linux内核中
Linux似乎没有针对x86_64优化的memcmp版本,但是它有一个针对memcpy优化的版本,在arch/x86/lib/memcpy_64.S中。需要注意的是它使用了alternatives基础设施(arch/x86/kernel/alternative.c)不仅在运行时决定使用哪个版本,而且还会进行自我修补以保证该决策只在启动时进行一次。