更快的strlen?

时间:2009-11-21 07:08:33

标签: c algorithm string

典型的strlen()遍历第一个字符,直到找到\0。 这要求您遍历每个角色。 在算法意义上,它的O(N)。

在模糊定义输入的情况下,有没有更快的方法可以做到这一点。 例如:长度小于50,或长度大约为200个字符。

我想到了查找块,但没有得到任何优化。

9 个答案:

答案 0 :(得分:23)

不确定。在写字符串时记录长度。

答案 1 :(得分:20)

实际上,strlen的{​​{3}}是矢量化方法的一个有趣例子。它的特殊之处在于它不使用向量指令,但却找到了一种方法,只使用来自缓冲区的32或64位字的普通指令。

答案 2 :(得分:9)

显然,如果您的字符串具有已知的最小长度,您可以在该位置开始搜索。

除此之外,你无能为力;如果你尝试做一些聪明的事情并找到一个\0字节,你仍然需要检查字符串开头和那一点之间的每个字节,以确保没有早期的\0

这并不是说strlen无法优化。它可以是流水线的,并且可以通过每次比较来处理字大小或矢量块。在大多数体系结构中,这些和其他方法的某种组合将在天真的字节比较循环中产生实质的恒定因子加速。当然,在大多数成熟的平台上,系统strlen已经使用这些技术实现。

答案 3 :(得分:6)

答案简短:不。

更长的答案:你真的认为如果有一种更快的方法来检查准系统C字符串的字符串长度,那么像C字符串库那样常用的东西就不会合并它吗?

如果没有关于字符串的某些额外知识,您必须检查每个字符。如果您愿意维护这些附加信息,您可以创建一个struct,将长度存储为结构中的字段(除了字符串的实际字符数组/指针),在这种情况下,您可以然后使长度查找为常量时间,但每次修改字符串时都必须更新该字段。

答案 4 :(得分:4)

杰克,

strlen通过查找结尾'\ 0'来工作,这是从OpenBSD获取的实现:

size_t
strlen(const char *str)
{
        const char *s;

        for (s = str; *s; ++s)
                ;
        return (s - str);
}

现在,考虑到你知道长度大约是200个字符,如你所说。假设你从200开始并向上和向下循环以获得'\ 0'。你在204找到一个,这是什么意思?字符串是204个字符长?没有!它可以在那之前用另一个'\ 0'结束,你所做的就是超出界限。

答案 5 :(得分:3)

您可以尝试使用矢量化。不确定编译器是否能够执行它,但我手动完成(使用内在函数)。但它可以帮助你只为长串。

使用stl字符串,它更安全,std :: string类包含其长度。

答案 6 :(得分:3)

获取Core i7处理器。

Core i7附带SSE 4.2指令集。英特尔增加了四个额外的向量指令来加速strlen和相关的搜索任务。

以下是有关新指示的一些有趣想法:

http://smallcode.weblogs.us/oldblog/2007/11/

答案 7 :(得分:0)

在这里,我附加了glibc 2.29中的asm代码。我删除了ARM cpus的代码段。我测试了它,它真的很快,超出了我的预期。它只进行对齐,然后进行4字节比较。

ENTRY(strlen)
bic     r1, r0, $3              @ addr of word containing first byte
ldr     r2, [r1], $4            @ get the first word
ands    r3, r0, $3              @ how many bytes are duff?
rsb     r0, r3, $0              @ get - that number into counter.
beq     Laligned                @ skip into main check routine if no more
orr     r2, r2, $0x000000ff     @ set this byte to non-zero
subs    r3, r3, $1              @ any more to do?
orrgt   r2, r2, $0x0000ff00     @ if so, set this byte
subs    r3, r3, $1              @ more?
orrgt   r2, r2, $0x00ff0000     @ then set.
Laligned:               @ here, we have a word in r2.  Does it
tst     r2, $0x000000ff         @ contain any zeroes?
tstne   r2, $0x0000ff00         @
tstne   r2, $0x00ff0000         @
tstne   r2, $0xff000000         @
addne   r0, r0, $4              @ if not, the string is 4 bytes longer
ldrne   r2, [r1], $4            @ and we continue to the next word
bne     Laligned                @
Llastword:              @ drop through to here once we find a
tst     r2, $0x000000ff         @ word that has a zero byte in it
addne   r0, r0, $1              @
tstne   r2, $0x0000ff00         @ and add up to 3 bytes on to it
addne   r0, r0, $1              @
tstne   r2, $0x00ff0000         @ (if first three all non-zero, 4th
addne   r0, r0, $1              @  must be zero)
DO_RET(lr)

END(strlen)

答案 8 :(得分:0)

如果您控制字符串的分配,您可以确保不是只有一个终止 \0 字节,而是根据您平台的向量指令的最大大小连续多个。然后,您可以一次使用 X 个字节编写相同的 O(n) 算法,比较 0,使 strlen 分摊 O(n/X)。请注意,额外 \0 字节的数量不等于您的向量指令操作的字节数量 (X),而是 2*X - 1,因为对齐的区域应该用零填充。

不过,您通常需要在开始时迭代几个字节,直到到达与 X 字节边界对齐的地址。

这个用例有点不存在:您需要分配的额外字节数量很容易不仅仅是简单地存储一个简单的 4 或 8 字节整数,直接包含大小。即使由于某种原因这个字符串可以单独作为指针传递而不传递它的大小对你很重要,我认为在分配期间将大小存储为第一个 Y 字节可能是最快的。但这与您所询问的 strlen 优化相去甚远。

澄清:

the_size | the string ...
         ^
 the pointer to the string

glibc 实现更酷。