String 比较两个字符串(nul终止),而不是逐字节进行比较?

String 比较两个字符串(nul终止),而不是逐字节进行比较?,string,algorithm,assembly,string-comparison,String,Algorithm,Assembly,String Comparison,像glibc中的strlen()一样,它执行一个很好的位操作,每次检查4个字节,使函数速度如此之快,与大多数其他程序一样逐字节执行的例程相比,是否有类似的方法来比较汇编中的两个字符串?我正在阅读一些关于C语言代码实现的页面,对字符串处理部分非常感兴趣,但我仍然没有找到这样的页面。 我必须尽快实现这个函数,因为它是我应用程序的核心。(不推荐使用哈希表) 欢迎任何汇编器。但我对英特尔的汇编语法有点熟悉,如果您要提供的汇编不同,请发表评论。您可以逐字比较(例如,一次32位或64位)。你只需要小心不要超

像glibc中的
strlen()
一样,它执行一个很好的位操作,每次检查4个字节,使函数速度如此之快,与大多数其他程序一样逐字节执行的例程相比,是否有类似的方法来比较汇编中的两个字符串?我正在阅读一些关于C语言代码实现的页面,对字符串处理部分非常感兴趣,但我仍然没有找到这样的页面。 我必须尽快实现这个函数,因为它是我应用程序的核心。(不推荐使用哈希表)


欢迎任何汇编器。但我对英特尔的汇编语法有点熟悉,如果您要提供的汇编不同,请发表评论。

您可以逐字比较(例如,一次32位或64位)。你只需要小心不要超过绳子的末端。如果要生成字符串,则可以将其填充为零,使其为单词大小的倍数,甚至无需进行检查。

StrCompCase(区分大小写),如中所实现 下面是一些使用dword比较的代码:

注意,这首先检查字符串的长度。这是因为在所提到的库中,字符串以长度为前缀,因此StrLen是即时的O(1),而对终止NULL的扫描只是作为一种回退提供的(请参阅本答案的第二部分)

在实际比较之前比较长度可以使不同字符串的速度为O(1),这在搜索大数组的情况下可以显著提高性能

然后对DWORD进行比较,最后,如果字符串长度不是4的倍数,则对剩余的1..3字节逐字节进行比较

proc StrCompCase, .str1, .str2
begin
        push    eax ecx esi edi

        mov     eax, [.str1]
        mov     ecx, [.str2]

        cmp     eax, ecx
        je      .equal

        test    eax, eax
        jz      .noteq

        test    ecx, ecx
        jz      .noteq

        stdcall StrLen, eax
        push    eax
        stdcall StrLen, ecx

        pop     ecx
        cmp     eax, ecx
        jne     .noteq

        stdcall StrPtr, [.str1]
        mov     esi,eax
        stdcall StrPtr, [.str2]
        mov     edi,eax

        mov     eax, ecx
        shr     ecx, 2
        repe cmpsd
        jne     .noteq
        mov     ecx, eax
        and     ecx, 3
        repe cmpsb
        jne     .noteq

.equal:
        stc
        pop     edi esi ecx eax
        return

.noteq:
        clc
        pop     edi esi ecx eax
        return
endp
关于StrLen代码: 下面是StrLen的实现

您可以看到,如果可能,它使用长度前缀字符串,这样使执行时间为O(1)。如果这是不可能的,它会退回到扫描算法,每个周期检查8个字节,速度相当快,但仍然是O(n)

请注意,以零结尾的字符串同时存在性能和安全问题


最好使用大小前缀字符串。例如,所提到的库使用动态字符串,其中字符串包含偏移量-4处的dword字段(上述代码中的string.len),该字段包含字符串的当前长度。

比字节/字节比较快的第一条规则是malloc字符串或
。对齐16
任何常量字符串以确保

  • 对安全违规的鲁棒性(读取超过分配的区域)
  • xxm(或64位)处理的最佳对齐方式

假设以零结尾的字符串(尽管这同样适用于
memcmp()
);在汇编中进行字符串比较的最快方法取决于字符串的长度/秒以及特定的CPU

一般而言;SSE或AVX的安装成本很高,但一旦运行,吞吐量会更快,这使得它成为比较很长字符串(尤其是在大多数字符匹配的情况下)的最佳选择

或者,使用通用寄存器一次执行一个字节的操作通常具有非常低的设置成本和较低的吞吐量,这使得它成为比较大量小字符串(甚至是大量大字符串,其中前几个字符可能不同)的最佳选择

如果您是针对特定应用程序执行此操作,则可以确定比较的平均字符数,并找到该平均数的最佳方法。您还可以针对不同的情况使用不同的函数,例如,如果混合使用,则实现
strcmp\u small()
strcmp\u large()


尽管如此,如果字符串比较的性能非常重要,那么比较字符串的最快方法很可能是根本不比较字符串。基本上,“我必须使这个函数尽可能快,因为它是我的应用程序的核心”这句话应该让每个人都想知道为什么实现这个应用程序的更好方法是不可能的。

字符串末尾的零?即“abcc”,0,0而不是“abcc”,0?是的,零字节(NUL chars),因此字节中的字符串长度最终是4的倍数,如果一次比较64位字,则是8的倍数。当然,对于大多数应用程序来说,这种级别的优化可能不是必需的:)有什么提示如何在C中实现它吗?我用0将字符串对齐到4个字节,但简单的比较不起作用。字节以相反的顺序读取,abc0类似0cba。。。代码示例:unsigned int*s1=(unsigned int*)str1;如果(*s1<*s2)返回-1
proc StrLen, .hString    ; proc StrLen [hString]
begin
        mov     eax, [.hString]
        cmp     eax, $c0000000
        jb      .pointer

        stdcall StrPtr, eax
        jc      .error

        mov     eax, [eax+string.len]
        clc
        return

.error:
        xor     eax, eax
        stc
        return

.pointer:
        push    ecx edx esi edi

; align on dword
.byte1:
        test    eax, 3
        jz      .scan

        cmp     byte [eax], 0
        je      .found

        inc     eax
        jmp     .byte1

.scan:
        mov     ecx, [eax]
        mov     edx, [eax+4]

        lea     eax, [eax+8]

        lea     esi, [ecx-$01010101]
        lea     edi, [edx-$01010101]

        not     ecx
        not     edx

        and     esi, ecx
        and     edi, edx

        and     esi, $80808080
        and     edi, $80808080

        or      esi, edi
        jz      .scan

        sub     eax, 9

; byte 0 was found: so search by bytes.
.byteloop:
        lea     eax, [eax+1]
        cmp     byte [eax], 0
        jne     .byteloop

.found:
        sub     eax, [.hString]
        clc
        pop     edi esi edx ecx
        return
endp