为了磨练我的C技能,我下载了eglibc源代码,发现了strncpy。我不明白他为什么区分n<4,并进行了4次测试。
int
STRNCMP (const char *s1, const char *s2, size_t n)
{
unsigned char c1 = ' ';
unsigned char c2 = ' ';
if (n >= 4)
{
size_t n4 = n >> 2;
do
{
c1 = (unsigned char) *s1++;
c2 = (unsigned char) *s2++;
if (c1 == ' ' || c1 != c2)
return c1 - c2;
c1 = (unsigned char) *s1++;
c2 = (unsigned char) *s2++;
if (c1 == ' ' || c1 != c2)
return c1 - c2;
c1 = (unsigned char) *s1++;
c2 = (unsigned char) *s2++;
if (c1 == ' ' || c1 != c2)
return c1 - c2;
c1 = (unsigned char) *s1++;
c2 = (unsigned char) *s2++;
if (c1 == ' ' || c1 != c2)
return c1 - c2;
} while (--n4 > 0);
n &= 3;
}
while (n > 0)
{
c1 = (unsigned char) *s1++;
c2 = (unsigned char) *s2++;
if (c1 == ' ' || c1 != c2)
return c1 - c2;
n--;
}
return c1 - c2;
}
可能与我不知道的内存布局有关,请启发我。
这是一个展开的循环。以使二进制文件稍微大一点为代价,它通过为每4个要比较的字节消除3个递减、3个分支和3个条件来加快字符串比较。
使用与达夫的设备相同的技术,优化甚至可以更进一步,尽管尚不清楚这是否会更快。从链接页面,
这种对余数的自动处理可能不是所有系统和编译器的最佳解决方案——在某些情况下,两个循环实际上可能更快(一个循环展开,用于执行主复制,第二个循环用于处理余数)。问题似乎归结为编译器正确优化设备的能力;它还可能干扰某些体系结构上的流水线和分支预测。当Duff设备的许多实例在4.0版本中从XFree86服务器中删除时,性能得到了提高,可执行文件的大小也明显减小。因此,在考虑使用此代码时,可能值得运行一些基准测试,以验证它在目标编译器的目标优化级别上实际上是目标体系结构上最快的代码。