【问题标题】:Can someone explain why this function is the way it is?有人可以解释为什么这个功能是这样的吗?
【发布时间】:2013-12-15 08:28:52
【问题描述】:

我正在查看来自 GNU C 库 glibc-2.18 的函数,这是我为 strncmp.c 找到的代码

看着它,我不明白为什么要这样写。这个循环展开了吗? 为什么不使用 5 或 10 而不是 4? 为什么他们这样写而不是使用更直接的方法?

/* Compare no more than N characters of S1 and S2,
   returning less than, equal to or greater than zero
   if S1 is lexicographically less than, equal to or
   greater than S2.  */
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;
}

谁能解释一下代码背后的逻辑?

谢谢。

【问题讨论】:

  • @Mat 看不到任何 Duff 关于这段代码的内容。
  • 是的,这看起来像是手动展开的循环。我们的想法是它会更快,因为更少的测试和跳转。

标签: c gnu strncmp


【解决方案1】:

是的,它是一个部分展开的循环。

4:

1) 一般大小:在更多分支(小数字)和更大代码大小(大数字)之间总是存在权衡。

2)具体大小(4,而不是 5):正如 Joachim Isaksson 指出的 >> 如果选择 5 而不是 4,则循环需要更改为除法之前。他说在某些(例如嵌入式) CPU 变得重要。 (通常我们只考虑循环的成本,而不是它的设置,但他可能是对的,因为大多数字符串往往都很小!)

请注意,如果您想将循环展开为任何数字(无论是否为 2 的幂),那么您可以 - 而不是进行除法并保持 n4 - 您可以计算s1 的结束指针并检查 s1 与最后的对比:

char* s1end = s1 + n;
do {...} while(s1 < s1end);

【讨论】:

  • 用 4 而不是 5 的原因很可能是除以 4 计算起来很简单,而除以 5 不一定在所有机器上。
  • @JoachimIsaksson 循环中没有除法
  • 不,但是除以 4 size_t n4 = n &gt;&gt; 2; 和循环外的 mod 操作 n &amp;= 3;。作为比较,在没有专用除法指令的 CPU 上除以 5 可能会完全抵消所有速度增益。
  • @JoachimIsaksson 你是对的,如果他们在考虑那种 CPU,这可能是 4 的原因。
【解决方案2】:

这种方式有更少的分支(if 语句),它允许编译器针对管道进行优化。

【讨论】:

    【解决方案3】:

    是的,这是循环展开。

    为什么是 4 而不是 5 或 10。可能是因为这是为了缓存友好并确保调用与内存页/行对齐。

    问题可能是为什么不是 8、16 或 32。也许是代码大小,也许是 4 时更容易为某些旧处理器生成优化的程序集。或者实际代码太大而无法容纳典型的指令缓存。

    【讨论】:

      猜你喜欢
      • 2012-06-15
      • 2022-01-22
      • 2021-05-27
      • 1970-01-01
      • 2011-02-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-24
      • 2012-01-14
      相关资源
      最近更新 更多