C-交换两个大小相等的内存块的最快方法?(解决方案可行性)

C-交换两个大小相等的内存块的最快方法?(解决方案可行性),c,memory,swap,avx2,C,Memory,Swap,Avx2,这个问题是问题的延伸。这里我提出两种可能的解决方案,我想知道它们的可行性。我将Haswell微体系结构与GCC/ICC编译器一起使用。我还假设内存是对齐的 选项1-我已经分配了一个内存位置,并进行了3次内存移动。(我使用memmove而不是memcpy来避免复制构造函数) 选项2使用AVX或AVX2加载和存储,利用对齐内存。对于这个解决方案,我考虑交换 int 数据类型。 void swap_memory(int *A, int* B, int NUM_ELEMS){ int i,

这个问题是问题的延伸。这里我提出两种可能的解决方案,我想知道它们的可行性。我将Haswell微体系结构与GCC/ICC编译器一起使用。我还假设内存是对齐的

选项1-我已经分配了一个内存位置,并进行了3次内存移动。(我使用
memmove
而不是
memcpy
来避免复制构造函数)


选项2使用AVX或AVX2加载和存储,利用对齐内存。对于这个解决方案,我考虑交换<代码> int <代码>数据类型。

void swap_memory(int *A, int* B, int NUM_ELEMS){

    int i, STOP_VEC = NUM_ELEMS - NUM_ELEMS%8;
    __m256i data_A, data_B;

    for (i=0; i<STOP_VEC; i+=8) {
        data_A = _mm256_load_si256((__m256i*)&A[i]);
        data_B = _mm256_load_si256((__m256i*)&B[i]);

        _mm256_store_si256((__m256i*)&A[i], data_B);
        _mm256_store_si256((__m256i*)&B[i], data_A);
    }

    for (; i<NUM_ELEMS; i++) {
        std::swap(A[i], B[i]);
    }
}
void swap_内存(int*A、int*B、int NUM_元素){
int i,STOP_VEC=NUM_ELEMS-NUM_ELEMS%8;
__m256i数据_A、数据_B;

对于(i=0;i如果您确信内存已对齐,则使用AVX可能是最好的。请注意,显式执行此操作可能不可移植-最好装饰指针,以便已知它们已对齐(例如,使用
aligned
属性或类似属性)

最有可能的是,选项2(或在语义上这样做的东西)可能更快,因为指针没有受到限制或其他任何东西。编译器可能不知道重新排序内存或保持“aux”不变是安全的

此外,选项2可能更线程安全,具体取决于aux的设置方式


使用一个本地临时文件和memcpy在块中或一次从临时文件到临时文件,甚至一次使用所有临时文件都可以,因为gcc可能能够将其矢量化。避免使用外部临时文件,并确保所有结构都按照对齐方式进行装饰。

选项2的读取次数较少,因此我希望它会更快(当然,这一切都取决于数据的大小,如果所有东西都放在缓存中,那么性能优势就会小得多)


您也可以使用AVX内置的_mm256 _stream _si256而不是存储(这样,在再次读取内存之前,您需要一个围栏)。

我只需执行以下操作:

unsigned char t; 
unsigned char *da = A, *db = B; 
while(TO_MOVE--) { 
   t = *da; 
   *da++ = *db; 
   *db++ = t; 
}

基于这一点,它非常清晰,而且乐观主义者很有可能做得很好。

你分析过它吗?我猜(在优化打开的情况下)gcc/icc会为你矢量化循环,而不是要求你手动执行。OP:“我使用memmove而不是memcpy来避免复制构造函数”→ 什么?这两个函数都只处理原始字节,都不使用复制(或移动)构造函数或赋值运算符。第一种方法可以正确地处理重叠的范围。但是,如果您需要进行所有复制,那么这可能更像是一个设计问题-您应该能够交换两个指针,当然?…请注意--如果指针标记为
\uuuuuuu restrict\uuuuuu
,我希望gcc/icc将循环矢量化为你。如果没有
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
,我不知道现在有多少编译器会添加非重叠范围的测试,以检查重新排序操作是否安全。我认为你不能使用
alignas
告诉gcc指针f(例如,
alignas(32)foo[32]
).Option2更好,因为编译器几乎肯定不会将memcpy版本优化为不涉及
aux
的交错循环。您的最后一段可能会以少量计数多次调用memcpy。那么可能是指向对齐存储的指针?我通常只使用gcc属性或定义等rgot认为alignas不是指向内存的东西。gcc特定的方式是
a=\uuuu内置\u假设\u对齐(a,32)
。您还可以
typedef\uu属性((对齐(32)))int align32\u int
,然后
void foo(align32\u int*a){
.IIRC,这确实有效,虽然同一个typedef中的alignas不触发,但typedef方法是我通常使用的方法。可惜alignas不能这样使用。如果缓冲区很小,您很快就会读取它,NT存储会更糟。它们会逐出目标,即使它在缓存中很热。这对自动矢量化很好(
-O3
),但是,即使在-O2,您也可以使用
-fopenmp
#pragma omp simd
。因为您有
int
和int的元素计数,将其转换为char是愚蠢的。这使得标量清理循环更糟糕。
unsigned char t; 
unsigned char *da = A, *db = B; 
while(TO_MOVE--) { 
   t = *da; 
   *da++ = *db; 
   *db++ = t; 
}