【问题标题】:/Does/ memmove use an intermediate buffer?/Does/ memmove 使用中间缓冲区吗?
【发布时间】:2014-04-07 14:11:40
【问题描述】:

这是一个出于好奇而不是任何重要的问题,但我只是想知道memmove 文档中的以下 sn-p:

复制发生就像使用了中间缓冲区

(强调我的)。该公式向我表明,是否使用中间缓冲区是特定于编译器实现的。

如果您要我写memmove,我可能会自动执行以下操作:

  • 在堆上分配n字节
  • memcpy temp 的来源
  • memcpy 到目的地的温度
  • 释放缓冲区

我希望任何人都可以......

  1. ... 确认该公式是否只是因为用户更容易可视化正在发生的事情,而没有修复特定实现实际上必须使用中间缓冲区;
  2. ... 阐明了一些常见 C++ 编译器(如 gcc 或 Visual C++)中的 实际 实现 - 例如,确实 它使用缓冲区它是否检查重叠,以便可以直接memcpy
  3. ...也许可以指出我上面的简单算法中明显的错误/效率低下。

【问题讨论】:

  • 据我了解,它的措辞是为了确保它正确处理在 SAME BUFFER 中正确复制的情况。在某些情况下,这似乎需要中间缓冲区。
  • 'As if' 表示源区域和目标区域重叠时数据不会被破坏。这可以通过在适当的方向复制来简单地实现,地址被递增或递减,具体取决于哪个部分重叠。但是,如果位移是 1 个字节,那么效果会急剧下降 - 数据必须按字节复制,而不是按 4 字节块复制。然后 CPU 寄存器可以用作缓冲区 - 但仍然不需要额外的内存缓冲区。
  • 谢谢@CiaPan,这就是我正在寻找的东西。使用缓冲区可以防止这种情况发生,但对于大的非重叠块来说效率低下。所以我想知道memmove在这方面有多少“智慧”来选择合适的策略。
  • 不需要太多的智慧。它将向前或向后迭代源字节,具体取决于目标如何与源重叠。

标签: c++ internals memmove


【解决方案1】:
  1. 确实如此。 “好像”意味着它必须表现得像它所做的那样;但并不限制实现实际做到这一点。唯一需要的行为是目标缓冲区以来自源缓冲区的正确字节结束,无论缓冲区是否重叠。

  2. 一个常见的实现是,如果目标在源之前开始,则从缓冲区的开头向前复制字节,否则从结尾向后复制。这样可以确保源字节在被覆盖之前总是被读取,如果有重叠的话。

  3. 没有错误,除非分配失败。效率低下是分配和释放临时缓冲区,并将每个字节复制两次而不是一次。

【讨论】:

    【解决方案2】:

    您在#1 上是绝对正确的 - 描述是为了帮助用户在逻辑上可视化正在发生的事情,而不是解释它是如何实现的。

    但是,没有任何合理的实现会真正使用昂贵的临时缓冲区来执行此操作,因为要避免重复复制,您需要做的就是决定是从头复制还是从尾复制。这是一个sample implementation,正是这样做的。

    您的算法的唯一问题是,它可能会在不必要时使您的系统内存不足:假设您的程序试图移动一个大小为允许的总内存的 60% 的缓冲区,以查看何时发生的示例会发生。

    【讨论】:

    • 有趣的是,您的示例实现实际上使用相同的代码进行复制和移动!
    • @CompuChip 为什么不呢?毕竟,副本在传递无效参数时不需要中断:-)
    • 是的,为什么不呢。直到我看到它才意识到这一点!
    【解决方案3】:

    首先想到的优化机会之一是如果缓冲区不重叠,则执行普通的 memcpy()。由于(虚拟)地址空间的平坦性质,很容易检查。我查看了glibcAndroid 的实现,它们都这样做了(Android 的初学者更容易理解)。

    在堆上分配内存可能是不行的,因为它会很慢(动态分配并不便宜)。

    如果缓冲区确实重叠,我们可以优化不重叠部分的复制,对于其余部分,我们可能会使用一个小的暂存缓冲区,但如果确实需要任何分配,那将是堆栈分配的。 Android 一次只复制一个字节;我们可以在 amd64 上做得更好,但这与 memcpy 中已经完成的优化相同。 glibc 根据重叠的性质向前或向后复制(这就是源中的“BWD”所指的)。

    【讨论】:

      【解决方案4】:

      实施无关紧要。措辞是为了保证正确处理内存。

      char buf[] = { 0x11, 0x22, 0x33, 0x00 };
      memcpy(buf, buf + 1, 3);
      

      可能导致buf 成为{ 0x11, 0x11, 0x11, 0x11 }

      在哪里

      char buf[] = { 0x11, 0x22, 0x33, 0x00 };
      memmove(buf, buf + 1, 3);
      

      保证buf 将是{ 0x11, 0x11, 0x22, 0x33 }

      【讨论】:

        猜你喜欢
        • 2010-12-29
        • 1970-01-01
        • 1970-01-01
        • 2014-03-26
        • 2011-11-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多