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