3

为了磨练我的 C 技能,我下载了 eglibc 源代码并遇到了 strncpy。我不明白他为什么区分 n<=4 的情况并进行 4 次测试。

int
STRNCMP (const char *s1, const char *s2, size_t n)
{
  unsigned char c1 = '\0';
  unsigned char c2 = '\0';

  if (n >= 4)
    {
      size_t n4 = n >> 2;
      do
    {
      c1 = (unsigned char) *s1++;
      c2 = (unsigned char) *s2++;
      if (c1 == '\0' || c1 != c2)
        return c1 - c2;
      c1 = (unsigned char) *s1++;
      c2 = (unsigned char) *s2++;
      if (c1 == '\0' || c1 != c2)
        return c1 - c2;
      c1 = (unsigned char) *s1++;
      c2 = (unsigned char) *s2++;
      if (c1 == '\0' || c1 != c2)
        return c1 - c2;
      c1 = (unsigned char) *s1++;
      c2 = (unsigned char) *s2++;
      if (c1 == '\0' || c1 != c2)
        return c1 - c2;
    } while (--n4 > 0);
      n &= 3;
    }

  while (n > 0)
    {
      c1 = (unsigned char) *s1++;
      c2 = (unsigned char) *s2++;
      if (c1 == '\0' || c1 != c2)
    return c1 - c2;
      n--;
    }

  return c1 - c2;
}

可能和我不知道的内存布局有关,请赐教。

4

1 回答 1

6

这是一个展开的循环。以使二进制文件稍大为代价,它通过消除每 4 个要比较的字节的 3 个递减、3 个分支和 3 个条件来加快字符串比较。

通过使用与Duff 的设备相同的技术,优化甚至可以更进一步,尽管目前尚不清楚这实际上是否会更快。从链接页面,

这种对剩余部分的自动处理可能不是所有系统和编译器的最佳解决方案——在某些情况下,两个循环实际上可能更快(一个循环,展开,做主副本,第二个循环处理剩余部分)。问题似乎归结为编译器正确优化设备的能力。它还可能会干扰某些架构上的流水线和分支预测。当 Duff 设备的许多实例从 4.0 版的 XFree86 服务器中删除时,性能得到了改进,并且可执行文件的大小明显减小。因此,在考虑使用此代码时,可能值得运行一些基准测试来验证它实际上是目标架构上最快的代码,在目标优化级别,使用目标编译器。

于 2016-05-04T21:44:00.840 回答