当前位置: 首页 > 知识库问答 >
问题:

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

房项禹
2023-03-14

为什么memcmp(a,b,size)比:

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

memcmp是CPU指令还是什么?它一定很深,因为我在循环中使用memcmp获得了巨大的加速。

共有3个答案

岳杜吟
2023-03-14

memcmp是CPU指令还是什么?

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

程和煦
2023-03-14

它通常是一个编译器的固有特性,被翻译成快速汇编,带有专门的指令,用于比较内存块。

内在memcmp

颛孙正谊
2023-03-14

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

GCC支持内置的memcmp(以及大量其他功能)。在GCC的某些版本/配置中,对memcmp的调用将被识别为\uuuu builtin\umemcmp。GCC不会向memcmp库函数发出调用,而是发出一些指令,作为函数的优化内联版本。

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

鉴于以下代码:

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

Cygwin上的gcc 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似乎没有针对x86_64的memcmp优化版本,但它在arch/x86/lib/memcpy_64中针对memcpy。S。请注意,is使用alternatives基础设施(arch/x86/kernel/alternative.c)不仅可以在运行时决定使用哪个版本,还可以在启动时对自身进行修补,只做一次决定。

 类似资料:
  • 问题内容: 我正在经历 递增/递减运算符 ,并且 遇到了这样的情况:如果在这种情况下以递减形式运行循环,则其运行速度将比相同的以递增形式运行的循环快。 我期望两者将花费相同的时间,因为将遵循相同数量的步骤。我在网上搜索,但找不到令人信服的答案。是因为与增量运算符相比,减数运算符花费的时间更少吗? 问题答案: 这是因为在字节码中,与0比较与与非零数字比较是不同的操作。实际需要先将数字加载到堆栈上,然

  • 问题内容: 我读到 增强的for循环 比普通的 for循环 更有效: http://developer.android.com/guide/practices/performance.html#foreach 当我搜索它们的效率之间的差异时,我发现的是:如果是普通的for循环,我们需要一个额外的步骤来找出数组的长度或大小等, 但这是唯一的原因,增强的for循环优于普通的for循环吗?在那种情况下,

  • 据我所知,流比传统的旧编程更快。 然而,当我运行下面的代码时,结果是我没有预料到的。 输出为:22857304

  • 问题内容: 我有这种方法,可以在登录前检查用户名和密码。现在,我的for循环仅检查第一个项目,它发现第一个项目不满足第一个条件,因此与其去检查第二个项目,它只是中断并返回null。 为什么会这样? 这是我的方法: 问题答案: 因此,请尝试此代码。

  • 问题内容: 我写了一个非常简单的基准: 如果您运行的是Chrome,则可以在此处进行尝试(因为NodeJS和Chrome使用相同的JavaScript引擎,尽管通常版本略有不同): 结果令我惊讶: 我已经在Node 4.0.0 && 5.0.0 && 6.0.0中对其进行了测试,每个节点版本之间的和比例相同。 有人可以向我解释这种看似奇怪的行为的原因是什么? 问题答案: 基于vs. 的机制差异,这

  • 我有两个gcd函数的实现: 函数gcd1是尾递归的,而gcd2使用的是时循环。 我已经验证了rubinius通过对阶乘函数进行基准测试来实现TCO,只有通过阶乘函数,基准测试才表明递归版本和迭代版本是“相同的ish”(我使用了基准测试IP)。 但对于上述情况,基准测试表明,gcd1比gcd2快至少两倍(递归比迭代快两倍,甚至更快)。 我用来基准测试的代码是这样的: 结果: 我正在运行Arch li