strstr比算法快?

Jos*_*osh 16 c algorithm performance string-matching strstr

我有一个21056字节的文件.

我在C中编写了一个程序,将整个文件读入缓冲区,然后使用多个搜索算法在文件中搜索82个字符的标记.

我已经使用了"精确字符串匹配算法"页面中所有算法的实现.我用过:KMP,BM,TBM和Horspool.然后我使用strstr并对每一个进行基准测试.

我想知道的是,每次strstr优于所有其他算法.有时候唯一更快的是BM.

strstr应该是最慢的?

这是我的基准代码,其中包含基准测试BM的示例:

double get_time()
{
    LARGE_INTEGER t, f;
    QueryPerformanceCounter(&t);
    QueryPerformanceFrequency(&f);
    return (double)t.QuadPart/(double)f.QuadPart;
}
Run Code Online (Sandbox Code Playgroud)
before = get_time();
BM(token, strlen(token), buffer, len);
after = get_time();
printf("Time: %f\n\n", after - before);
Run Code Online (Sandbox Code Playgroud)

有人可以向我解释为什么strstr表现优于其他搜索算法吗?如果需要,我会根据请求发布更多代码.

Ton*_*nyK 29

为什么你认为strstr应该比其他所有人慢?你知道算法strstr使用什么吗?我认为很可能strstr使用KMP类型或更好的微调,处理器特定,汇编编码算法.在这种情况下,你没有机会在C这么小的基准测试中表现出色.

(我认为这很可能是因为程序员喜欢实现这样的东西.)

  • [glibc的实施](http://www.google.com/codesearch#xy1xtVWIKOQ/pub/glibc/snapshots/glibc-latest.tar.bz2%7CXP6Z3zoy3dk/glibc-20090518/string/str-two-way.h&q=str - 两个-way.h&类型= CS).它不是汇编程序,但我确信它已经过大量优化. (6认同)
  • @Konrad:啊,我看到算法描述在我链接的页面上有点令人困惑.但它确实是一种无弱点算法:恒定时间和线性空间.应该更好地了解和欣赏它的存在!:)更重要的是,它应该是正确的:在`strstr()`的库实现中.:) (4认同)
  • @Josh:不,不要被误导! (3认同)
  • @Josh确实如此,不要被误导. (2认同)
  • @Banthar让我感到惊讶.我*看过多个'strstr`实现(过去几年我在字符串搜索实现上做了很多工作),我看到的每个实现都是天真的搜索 - 顺便说一句,这是一个很好的选择*(参见以上).我也很惊讶他们使用BM而不是Horspool作为长针,因为后者在典型情况下平均表现更好(再次见上文). (2认同)

Mis*_*cha 16

Horspool,KMP等人在最小化字节比较次数方面是最佳的.

然而,这不是现代处理器的瓶颈.在x86/64处理器上,您的字符串将以高速缓存行宽度块(通常为64字节)加载到L1高速缓存中.无论你的算法多么聪明,除非它给你的步幅大于那个,你什么都得不到; 而更复杂的Horspool代码(至少有一个表查找)无法竞争.

此外,你仍然坚持使用null-termination的"C"字符串约束:SOMEWHERE代码必须检查每个字节.

strstr()预计对于各种病例都是最佳的; 例如,搜索"\r\n"短字符串中的小字符串,以及更长的字符串,其中一些更智能的算法可能有希望.基本的strchr/memcmp循环很难在整个可能的输入范围内击败.

几乎所有x86兼容处理器自2003年以来都支持SSE2.如果你strlen()glibc反汇编/ x86 ,你可能已经注意到它使用一些SSE2 PCMPEQ和MOVMASK操作来一次搜索16个字节的空终止符.该解决方案非常有效,它可以胜过明显的超简单循环,比空字符串更长.

我接受了这个想法并提出了一个比所有大于1字节的情况更好的strstr()glibc's strstr()---其中相对差异几乎没有实际意义.如果您有兴趣,请查看:

顺便说一句,你现在可能已经想到x86 REP SCASB/REP CMPSB操作对于长度超过32字节的任何内容都会出现问题,并且对于较短的字符串没有太大的改进.希望英特尔更多地关注这一点,而不是添加SSE4.2"字符串"操作.

对于足够重要的字符串,我的性能测试显示BNDM全面胜过Horspool.BNDM更能容忍"病态"情况,例如重复重复模式最后一个字节的目标.BNDM还可以以与32位寄存器竞争效率和启动成本的方式使用SSE2(128位寄存器).源代码在这里.

  • 你有没有尝试过向glibc提交补丁?当然,这需要在广泛的不同输入上进行仔细的基准测试,但您的上述解释似乎是合理的. (2认同)