首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >C-交换大小相等的两个内存块的最快方法?

C-交换大小相等的两个内存块的最快方法?
EN

Stack Overflow用户
提问于 2011-11-17 19:41:02
回答 9查看 10.9K关注 0票数 12

交换两个大小相等的非重叠内存区域的最快方法是什么?比方说,我需要用(t_Some *b)交换(t_Some *a)。考虑到时空权衡,增加临时空间会提高速度吗?例如,(char *tmp)(int *tmp)?我正在寻找一种便携解决方案。

原型:

代码语言:javascript
运行
复制
void swap_elements_of_array(void* base, size_t size_of_element, int a, int b);
EN

回答 9

Stack Overflow用户

回答已采纳

发布于 2011-11-17 19:57:10

最好的办法是最大限度地使用寄存器,这样在读取临时数据时,就不会出现额外的(可能是缓存的)内存访问。寄存器的数量将取决于系统,寄存器分配(将变量映射到实际寄存器的逻辑)将取决于编译器。所以你最好的选择是,我想只需要一个寄存器,并且它的大小和指针一样。这归结为一个简单的for循环,它处理被解释为size_t数组的块。

票数 5
EN

Stack Overflow用户

发布于 2018-02-16 08:05:48

移动内存块的最快方法是使用<string.h>中的memcpy()。如果您从a memcpy()temp,从ba执行memmove(),然后从temp执行memcpy()b,那么将有一个使用优化库例程的交换,编译器可能会内联这些库例程。您不希望一次复制整个块,而是以向量大小的块为单位。

在实践中,如果您编写了一个紧凑的循环,编译器可能会知道您正在交换数组的每个元素,并相应地进行优化。在大多数现代CPU上,您希望生成向量指令。如果您确保所有三个缓冲区都对齐,它可能会生成更快的代码。

然而,您真正想要做的是让优化器的工作变得更容易。以这个程序为例:

代码语言:javascript
运行
复制
#include <stddef.h>

void swap_blocks_with_loop( void* const a, void* const b, const size_t n )
{
  unsigned char* p;
  unsigned char* q;
  unsigned char* const sentry = (unsigned char*)a + n;

  for ( p = a, q = b; p < sentry; ++p, ++q ) {
     const unsigned char t = *p;
     *p = *q;
     *q = t;
  }
}

如果你把它翻译成字面上写的机器代码,这是一个糟糕的算法,一次复制一个字节,每次迭代进行两次递增,等等。但在实践中,编译器会看到您真正想要做的事情。

在带有-std=c11 -O3的clang 5.0.1中,它在x86_64上产生(部分)以下内循环:

代码语言:javascript
运行
复制
.LBB0_7:                                # =>This Inner Loop Header: Depth=1
        movups  (%rcx,%rax), %xmm0
        movups  16(%rcx,%rax), %xmm1
        movups  (%rdx,%rax), %xmm2
        movups  16(%rdx,%rax), %xmm3
        movups  %xmm2, (%rcx,%rax)
        movups  %xmm3, 16(%rcx,%rax)
        movups  %xmm0, (%rdx,%rax)
        movups  %xmm1, 16(%rdx,%rax)
        movups  32(%rcx,%rax), %xmm0
        movups  48(%rcx,%rax), %xmm1
        movups  32(%rdx,%rax), %xmm2
        movups  48(%rdx,%rax), %xmm3
        movups  %xmm2, 32(%rcx,%rax)
        movups  %xmm3, 48(%rcx,%rax)
        movups  %xmm0, 32(%rdx,%rax)
        movups  %xmm1, 48(%rdx,%rax)
        addq    $64, %rax
        addq    $2, %rsi
        jne     .LBB0_7

而具有相同标志的gcc 7.2.0也进行了矢量化,更少地展开循环:

代码语言:javascript
运行
复制
.L7:
        movdqa  (%rcx,%rax), %xmm0
        addq    $1, %r9
        movdqu  (%rdx,%rax), %xmm1
        movaps  %xmm1, (%rcx,%rax)
        movups  %xmm0, (%rdx,%rax)
        addq    $16, %rax
        cmpq    %r9, %rbx
        ja      .L7

说服编译器生成一次只处理一个单词的指令,而不是向量化循环,这与您想要的相反!

票数 6
EN

Stack Overflow用户

发布于 2016-02-24 00:48:14

Word写入将是最快的。但是,块大小和对齐都需要考虑。在实践中,事情通常是明智地对齐的,但你不应该指望它。memcpy()可以安全地处理所有事情,并且可以在合理的范围内针对固定大小进行专用(内置)。

这是一个可移植的解决方案,在大多数情况下都工作得相当好。

代码语言:javascript
运行
复制
static void swap_byte(void* a, void* b, size_t count)
{
    char* x = (char*) a;
    char* y = (char*) b;

    while (count--) {
        char t = *x; *x = *y; *y = t;
        x += 1;
        y += 1;
    }
}

static void swap_word(void* a, void* b, size_t count)
{
    char* x = (char*) a;
    char* y = (char*) b;
    long t[1];

    while (count--) {
        memcpy(t, x, sizeof(long));
        memcpy(x, y, sizeof(long));
        memcpy(y, t, sizeof(long));
        x += sizeof(long);
        y += sizeof(long);
    }
}

void memswap(void* a, void* b, size_t size)
{
    size_t words = size / sizeof(long);
    size_t bytes = size % sizeof(long);
    swap_word(a, b, words);
    a = (char*) a + words * sizeof(long);
    b = (char*) b + words * sizeof(long);
    swap_byte(a, b, bytes);
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8166502

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档