2

我知道memmove在 C (cstring 库)中处理重叠很好“以较慢的运行时间为代价”(见这篇文章)。我想知道为什么这个额外的运行时成本?在我看来,任何重叠问题都可以通过向后而不是向前复制来解决,我错了吗?

作为一个玩具示例,这里有两个版本的“右移”函数,它将数组的内容向右移动一个元素:

// Using memmove
template <typename T>
void shift_right( T *data, unsigned n )
{
    if (n)
    {
        data[n-1].~T();
        memmove( data+1, data, (n-1)*sizeof(T) );
        new (data) T();
    }
}

// Using copy_backward
template <typename Iterator>
void shift_right( Iterator first, Iterator last )
{
    Iterator it = last;
    std::copy_backward( first, --it, last );
}

它们是等价的吗?性能方面,哪一个最好用?


注意:从@DieterLücking 的评论来看,尽管采取了预防措施,但memmove在这种情况下使用上述版本是不安全的。

4

4 回答 4

7

假设一个好的实现,唯一的“额外成本”memmove是初始检查(添加和比较和分支)来决定是从前到后还是从后到前复制。这个成本完全可以忽略不计(添加和比较将被 ILP 隐藏,并且在正常情况下分支是完全可预测的),在某些平台上,memcpy它只是memmove.

期待您的下一个问题(“如果 memcpy 不比 memmove 快得多,为什么它存在?”),有几个很好的理由可以保留memcpy。在我看来,最好的一个是某些 CPU 本质上将 memcpy 实现为一条指令(rep/movs例如在 x86 上)。这些硬件实现通常有一个首选(快速)的操作方向(或者它们可能只支持一个方向的复制)。编译器可以自由地memcpy用最快的指令序列替换,而不用担心这些细节;它不能对memmove.

于 2014-03-03T21:44:29.390 回答
2

复制或移动的适当方法是 std::copy、std::copy_n、std::copy_backward 和 std::move。如果适用,适当的 C++ 库将使用 memcpy 或 memmove。因此,如果复制或移动的序列不包含无关紧要的数据,则无需寻找未定义的结果。

注意:这里的 std::move 是模板 'OutputIterator move(InputIterator first, InputIterator last, OutputIterator result);' (对于@Void)

于 2014-03-03T22:22:45.737 回答
2

Memmove 为您确定是向后复制还是向前复制;它还针对此任务进行了高度优化(即尽可能多地复制 SSE 优化块)。

您不太可能通过调用任何通用 STL 算法来做得更好(他们能做的最好的事情是在幕后调用 memcopy 或 memmove),但是您当然可以通过运行代码并对其计时来回答这个问题。

于 2014-03-03T21:46:35.573 回答
2

从您实际链接的帖子中(强调我的):

memcpy 只是循环,而 memmove 执行测试以确定循环的方向以避免损坏数据。这些实现相当简单。大多数高性能实现更复杂(涉及一次复制字大小的块而不是字节)。

于 2014-03-03T21:47:04.803 回答