如何编写更好的strlen函数?

我正在阅读“编写伟大的代码卷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函数的标准库实现,我无法理解它的大部分内容。 在哪里可以找到有关如何编写高度优化代码的更好信息?

来自优化strlen() ,Colm MacCarthaigh撰写的一篇博文:

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

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

有时真的很快就会让你真的疯了。

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

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

对于初学者来说,这对于像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, ie twice more for 32 bit */ size += sizeof (int); } 

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

底线:

伟大的代码强调除了最关键性能的案例之外的所有可读性 。 尽可能清楚地编写代码,只优化被certificate是瓶颈的部分。

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

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

以下应该比天真算法更快并且适用于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_(pc, LSIZE); p.c++ ) if (*pc == 0) return pc - base; while ( !(mask = pzerobl_(*pl)) ) p.l++; return pc - base + maskffs_(mask); }