Самый быстрый способ переместить блок памяти будет memcpy()
из <string.h>
. Если вы memcpy()
от a
до temp
, memmove()
от b
до a
, затем memcpy()
от temp
до b
, у вас будет своп, который использует оптимизированную библиотеку подпрограммы, которые компилятор, вероятно, встроен Вам не захочется копировать весь блок сразу, но кусками векторного размера.
На практике, если вы пишете жесткий цикл, компилятор, вероятно, скажет, что вы меняете каждый элемент массивов и соответственно оптимизируете. На большинстве современных процессоров вы хотите генерировать векторные инструкции. Он может генерировать более быстрый код, если вы убедитесь, что все три буфера выровнены.
Однако, что вы действительно хотите сделать, так это упростить работу оптимизатора. Возьми эту программу:
#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;
}
}
Если вы переводите это в машинный код как буквально написанный, это ужасный алгоритм, копирующий один байт за раз, делающий два приращения за итерацию и так далее. На практике, однако, компилятор видит, что вы действительно пытаетесь сделать.
В clang 5.0.1 с -std=c11 -O3
он создает (частично) следующий внутренний цикл на x86_64:
.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 с теми же флагами также векторизуется, меньше разворачивая цикл:
.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
Убедить компилятор создавать инструкции, работающие с одним словом за раз, вместо векторизации цикла, - это противоположность того, что вы хотите!