7

我想在时间关键函数中复制一个相对较短的内存序列(小于 1 KB,通常为 2-200 字节)。在 CPU 方面最好的代码似乎是rep movsd. 但是,我不知何故无法让我的编译器生成此代码。我希望(我隐约记得看到过)使用 memcpy 会使用编译器内置内在函数来做到这一点,但基于反汇编和调试,编译器似乎正在使用对 memcpy/memmove 库实现的调用。我还希望编译器可能足够聪明,能够识别后续循环并rep movsd自行使用,但似乎没有。

char *dst;
const char *src;
// ...
for (int r=size; --r>=0; ) *dst++ = *src++;

除了使用内联汇编之外,还有什么方法可以让 Visual Studio 编译器生成rep movsd序列吗?

4

6 回答 6

6

有几个问题浮现在脑海。

首先,你怎么知道 movsd 会更快?您是否查看过它的延迟/吞吐量?x86 架构充满了不应该使用的粗鲁的旧指令,因为它们在现代 CPU 上效率不高。

其次,如果你使用std::copy而不是 memcpy 会发生什么?std::copy可能更快,因为它可以在编译时专门针对特定数据类型。

第三,您是否在项目属性 -> C/C++ -> 优化下启用了内部函数?

当然,我假设也启用了其他优化。

于 2009-07-16T13:10:22.387 回答
4

您是否正在运行优化的构建?除非启用优化,否则它不会使用内在函数。还值得注意的是,它可能会使用比 rep movsd 更好的复制循环。它至少应该尝试使用 MMX 来执行一次 64 位的复制。事实上,在 6 或 7 年前,我写了一个 MMX 优化的复制循环来做这种事情。不幸的是,编译器的内在 memcpy 比我的 MMX 副本高出大约 1%。这真的教会了我不要对编译器正在做什么做出假设。

于 2009-07-16T12:59:22.697 回答
3

使用具有恒定大小的 memcpy

同时我发现:

当复制的块大小在编译时已知时,编译器将使用内在函数。如果不是,则调用库实现。当大小已知时,生成的代码非常好,根据大小进行选择。根据需要,它可以是单个 mov,或 movsd,或 movsd 后跟 movsb。

看来,如果我真的想始终使用 movsb 或 movsd,即使使用“动态”大小,我也必须使用内联汇编或特殊内在函数(见下文)。我知道大小“很短”,但编译器不知道,我无法与它沟通 - 我什至尝试使用 __assume(size<16),但这还不够。

演示代码,使用“-Ob1”编译(仅内联扩展):

  #include <memory.h>

  void MemCpyTest(void *tgt, const void *src, size_t size)
  {
    memcpy(tgt,src,size);
  }

  template <int size>
  void MemCpyTestT(void *tgt, const void *src)
  {
    memcpy(tgt,src,size);
  }

  int main ( int argc, char **argv )
  {
    int src;
    int dst;
    MemCpyTest(&dst,&src,sizeof(dst));
    MemCpyTestT<sizeof(dst)>(&dst,&src);
    return 0;
  }

专门的内在函数

我最近发现存在一种非常简单的方法,如何使用 movsd 使 Visual Studio 编译器复制字符 - 非常自然和简单:使用内在函数。以下内在函数可能会派上用场:

于 2009-07-16T13:18:11.023 回答
0

你给memcpy计时了吗?在最新版本的 Visual Studio 中,memcpy 实现使用 SSE2...,它应该比rep movsd. 如果您要复制的块是 1 KB,那么编译器没有使用内在函数并不是真正的问题,因为与复制时间相比,函数调用的时间可以忽略不计。

于 2009-07-16T13:04:51.573 回答
-1

请注意,为了使用movsd,src必须指向与 32 位边界对齐的内存,并且其长度必须是 4 字节的倍数。

如果是,为什么您的代码使用char *而不是int *什么?如果不是,你的问题是没有实际意义的。

如果你改成char *int *可能会得到更好的结果std::copy

编辑:您是否测量过复制是瓶颈?

于 2009-07-16T13:02:03.867 回答
-1

使用内存。这个问题已经解决了。

仅供参考,rep movsd 并不总是最好的,rep movsb 在某些情况下可能会更快,并且使用 SSE 等最好的是 movntq [edi], xmm0。您甚至可以通过将数据移动到缓冲区然后将其移动到目标来进一步优化使用页面局部性的大量内存。

于 2009-07-16T13:12:30.780 回答