为什么memcmp比for循环检查快得多?

为什么memcmp(a, b, size)比以下快得多:

 for(i = 0; i < nelements; i++) { if a[i] != b[i] return 0; } return 1; 

memcmp是CPU指令还是什么? 它必须非常深,因为我在循环中使用memcmp获得了大量的加速。

memcmp通常在汇编中实现,以利用许多特定于体系结构的特性,这可以使它比C中的简单循环快得多。

作为“内置”

GCC支持memcmp (以及许多其他function)作为内置 。 在GCC的某些版本/配置中,对memcmp的调用将被识别为__builtin_memcmp 。 GCC不会发出对memcmp库函数的call ,而是发出一些指令作为函数的优化内联版本。

在x86上,这利用了cmpsb指令,该指令将一个内存位置的字节串与另一个内存位置进行比较。 这与repe前缀相结合,因此比较字符串直到它们不再相等,或者计数耗尽。 (正是memcmp作用)。

给出以下代码:

 int test(const void* s1, const void* s2, int count) { return memcmp(s1, s2, count) == 0; } 

Cygwin上的gcc version 3.4.4生成以下程序集:

 ; (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中,有x86_64的memcmp版本可以利用以下指令集扩展:

  • 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 。 请注意,它使用备用基础结构( arch/x86/kernel/alternative.c ),不仅在运行时决定使用哪个版本,而且实际上修补自身仅在启动时做出此决定。

它通常是一个编译器内在函数,它被转换为快速汇编,并带有用于比较内存块的专用指令。

内在的memcmp

memcmp是CPU指令还是什么?

它至少是一个非常高度优化的编译器提供的内部函数。 可能是一台或两台机器指令,具体取决于您未指定的平台。

是的,在intel硬件上,有一个用于这种循环的汇编指令。 运行时将使用它。 (我不记得,它有点像rep cmps[b|w] ,还取决于数据量)