如何编写更好的strlen函数?

时间:2011-07-05 14:30:21

标签: c string optimization pointers c-strings

我正在阅读“写大代码第2卷”,它显示了以下的strlen inslementation:

int myStrlen( char *s )
{
    char *start;
    start = s;
    while( *s != 0 )
    {
        ++s;
    }
    return s - start;
}

这本书说这种实现对于没有经验的C程序员来说是典型的。在过去的11年里,我一直在使用C语言进行编码,我无法在C中看到如何编写比这更好的函数(我可以想到在汇编中编写更好的东西)。如何在C中编写比这更好的代码?我看了glibc中strlen函数的标准库实现,我无法理解它的大部分内容。哪里可以找到有关如何编写高度优化代码的更好信息?

7 个答案:

答案 0 :(得分:14)

来自Optimising strlen(),Colm MacCarthaigh的博客文章:

  

不幸的是,在C中,我们注定了O(n)实现,最好的情况,但我们还没有完成......我们可以对n的大小做点什么。

它提供了一个很好的例子,你可以在哪个方向加快速度。还有另一个引用

  

有时真的非常快,只会让你真的疯了。

答案 1 :(得分:3)

维克多,看看这个:
http://en.wikipedia.org/wiki/Strlen#Implementation

P.S。你不理解glibc版本的原因可能是因为它使用位移来找到\ 0。

答案 2 :(得分:3)

对于初学者来说,这对于像UTF-8这样的编码来说毫无价值......也就是说,计算UTF-8字符串中的字符数会更复杂,而字节数当然也很容易例如,用ASCII字符串计算。

通常,您可以通过读入更大的寄存器来优化某些平台。由于到目前为止发布的其他链接没有这样的示例,这里有一些针对低端的伪伪代码:

int size = 0;
int x;
int *caststring = (int *) yourstring;
while (int x = *caststring++) {
  if (!(x & 0xff)) /* first byte in this int-sized package is 0 */ return size;
  else if (!(x & 0xff00)) /* second byte etc. */ return size+1;
  /* rinse and repeat depending on target architecture, i.e. twice more for 32 bit */
  size += sizeof (int);
}

答案 3 :(得分:3)

正如其他人所指出的,更快的算法会读取整个单词而不是单个字符,并使用bitwise operations来查找终止空值。如果采用这种方法,请注意字对齐指针,因为某些CPU架构不允许您从未对齐的地址读取字(即使在不需要对齐的体系结构上,它也是触发段错误的好方法)。

底线:

优秀代码强调可读性超过速度除了性能最关键的案例外。尽可能清楚地编写代码,只优化被证明是瓶颈的部分。

答案 4 :(得分:1)

读取与机器数据总线大小不同的变量是很昂贵的,因为机器只能读取该大小的变量。因此,无论何时请求不同大小的东西(比如说更小),机器必须工作以使其看起来像所请求大小的变量(如移位)。 因此,您最好以机器大小的单词读取数据,然后使用AND操作检查0。此外,在扫描字符串时,请确保从对齐的起始地址开始。

答案 5 :(得分:1)

回答OP关于在哪里找到如何编写性能代码的建议的问题,这里是关于编写优化C代码的MIT OpenCourse的link(查找页面左侧的“材料”链接)。

答案 6 :(得分:1)

以下内容应该比朴素算法快,适用于32/64位。

union intptr {
    char* c;
    long* l;
#define LSIZE sizeof(long)
};

#define aligned_(x, a) \
    ((unsigned long) (x) % (a) == 0)

#define punpktt_(x, from, to) \
    ((to) (-1)/(from) (-1)*(from) (x))
#define punpkbl_(x) \
    punpktt_(x, unsigned char, unsigned long)

#define plessbl_(x, y) \
    (((x) - punpkbl_(y)) & ~(x) & punpkbl_(0x80))
#define pzerobl_(x) \
    plessbl_(x, 1)

static inline unsigned long maskffs_(unsigned long x)
{
    unsigned long acc = 0x00010203UL;
    if (LSIZE == 8)
       acc = ((acc << 16) << 16) | 0x04050607UL;
    return ((x & -x) >> 7) * acc >> (LSIZE*8-8);
}

size_t strlen(const char* base)
{
    union intptr p = { (char*) base };
    unsigned long mask;

    for ( ; !aligned_(p.c, LSIZE); p.c++ )
        if (*p.c == 0)
            return p.c - base;

    while ( !(mask = pzerobl_(*p.l)) )
        p.l++;
    return p.c - base + maskffs_(mask);
}