我真的必须扫描内存两次来做memcmp + memcpy吗?

时间:2014-05-30 19:01:04

标签: c++ optimization

考虑以下功能:

int memcmp_and_memcpy(void * x, const void * y, size_t n) {
  int c = memcmp(x, y, n);
  memcpy(x, y, n);
  return c;
}

是否有可能更有效地做到这一点?扫描相关内存两次似乎效率低下。

编辑:我正在寻找一种适用于各种输入的工业强度解决方案,尤其是当xy对于第一个m字节相同时,其中0<<m<=n。矢量化是必须的。

5 个答案:

答案 0 :(得分:2)

是的,你可以更高效地完成它,但它需要工作。一方面,您可以通过编写自己的循环来轻松消除内存上的重复传递,这会对所有字节进行单次传递。

由于内存带宽的使用得到改善,在大型阵列上几乎肯定会更快。

对于小型副本,目前尚不清楚您是否会获胜,因为memcmp和memcpy的实现本身已经过优化,并且可以使用更大的数据单元而不仅仅是字节;一个天真的基于字节的实现可能会丢失。需要在比较和复制中重复相同类型的优化。

天真,未经测试:

int memcmpy(void *dest, const void *src, size_t size)
{ 
  unsigned char *d = dst;
  const unsigned char *s = src;
  int compare = 0;

  for (; size--; s++, d++) {
    int sv = *s;
    int dv = *d;

    /* Thansks to Ben Voigt: if dv == sv, we can 
       avoid checking for the first difference, and skip
       the data move from source to dest: */
    if (dv == sv)
      continue;

    if (!compare) {
       if (dv < sv)
         compare = -1;
       else if (dv > sv)
         compare = 1;
    }

    *d = sv;
  }

  return compare;
}

答案 1 :(得分:1)

除非您的内存块总是相等,否则memcmp永远不会读取整个区域:一旦找到第一个差异,它就会退出。代码读取整个块两次的唯一时间是块相等,在这种情况下,您可以完全跳过复制:

int memcmp_and_memcpy(void * x, const void * y, size_t n) {
    int c;
    if ((c = memcmp(x, y, n)) != 0) {
        memcpy(x, y, n);
    }
    return c;
}

此代码的最坏情况是当区域总是在最后一个字节中不同时。如果你有很大一部分这种情况,你可以考虑将这两个函数重写为一个,如果你的探查器告诉你它确实是一个瓶颈。但是,很难与memcpy的良好库实现竞争,因为它通常会被大量优化。

答案 2 :(得分:1)

这是我的尝试。在你发现第一个区别之前,没有必要复制任何东西;我认为由于需要保持缓存一致性,写入比读取更昂贵。这段代码很可能是内存带宽有限的,所以英雄的微优化无关紧要。但请注意,我没有测试它。

int memcmp_and_memcpy(char * x, const char * y, size_t n)
{
    if (n == 0) return 0;
    int c = 0;
    while (c == 0 && n != 0)
    {
        c = *x++ - *y++;
        --n;
    }
    memcpy(--x, --y, ++n);
    return c;
}

不幸的是,我不知道一个标准函数会告诉你这两个缓冲区在哪里或者这可能更简单。

答案 3 :(得分:0)

memcmp和memcpy非常优化。我的建议是将memcmp代码复制到您的项目中,并将返回值更改为x和y中第一个字符的位置。所以首先你要比较你的memcmp_2函数,然后从这个位置到最后的memcpy。

答案 4 :(得分:0)

为什么要复制已经确定的值相同的值?这不必要地污染了缓存。

这是Kaz的版本的修改,避免了不必要的副本(以及争用缓存行的独占所有权):

int memcmpy(void *dest, const void *src, size_t size)
{ 
  if (!size) return 0;

  unsigned char *d = dst;
  const unsigned char *s = src;
  int compare = 0;

  do {
    int sv = *s++;
    unsigned char& dv = *d;

    if (dv != sv) {
        if (!compare) compare = dv - sv;
        dv = sv;
    }
  } while (--size);

  return compare;
}