使用AVX逐步添加两个双阵列

时间:2014-11-29 17:03:46

标签: c++ c arrays avx

我需要一个函数来按顺序添加两个double数组的元素并将结果存储在第三个数组中。目前我使用(简化)

void add( double* result, const double* a, const double* b, size_t size) {
    memcpy(result, a, size*sizeof(double));
    for(size_t i = 0; i < size; ++i) {
        result[i] += b[i];
    }
}

据我所知,memcpy函数使用AVX。为了提高性能,我还想强制使用AVX进行添加。这应该是AVX最基本的示例之一,但我在C / C++找不到任何说明。如果可能的话,我想避免使用外部库。

1 个答案:

答案 0 :(得分:7)

假设AVX-512:

,你需要这样的东西
void add( double* result, const double* a, const double* b, size_t size) 
{
    size_t i = 0;
    // Note we are doing as many blocks of 8 as we can.  If the size is not divisible by 8
    // then we will have some left over that will then be performed serially.
    // AVX-512 loop
    for( ; i < (size & ~0x7); i += 8) 
    {
        const __m512d kA8   = _mm512_load_pd( &a[i] );
        const __m512d kB8   = _mm512_load_pd( &b[i] );

        const __m512d kRes = _mm512_add_pd( kA8, kB8 );
        _mm512_stream_pd( &res[i], kRes );
    }

    // AVX loop
    for ( ; i < (size & ~0x3); i += 4 )
    {
        const __m256d kA4   = _mm256_load_pd( &a[i] );
        const __m256d kB4   = _mm256_load_pd( &b[i] );

        const __m256d kRes = _mm256_add_pd( kA4, kB4 );
        _mm256_stream_pd( &res[i], kRes );
    }

    // SSE2 loop
    for ( ; i < (size & ~0x1); i += 2 )
    {
        const __m128d kA2   = _mm_load_pd( &a[i] );
        const __m128d kB2   = _mm_load_pd( &b[i] );

        const __m128d kRes = _mm_add_pd( kA2, kB2 );
        _mm_stream_pd( &res[i], kRes );
    }

    // Serial loop
    for( ; i < size; i++ )
    {
        result[i] = a[i] + b[i];
    }
}

(虽然我被警告说,我只是把它从头顶抛到一起)。

上面代码需要注意的是我基本上使用下一个最好的并行代码处理剩余的值。这主要是为了说明您可以并行执行的3种可能方式。循环将自己完美地工作。例如,如果您不支持AVX-512,那么您可以直接跳转到AVX循环。如果您不能支持AVX,那么如果您直接跳到SSE2循环,那么您将使用硬件可以支持的最高性能循环。

为获得最佳性能,阵列应与加载中使用的相关大小对齐。因此对于AVX-512,您需要512位64字节对齐。对于AVX,256位或32字节对齐。对于SSE2 128位或16字节对齐。如果对所有阵列使用64字节对齐,那么你将始终保持良好的对齐,尽管你可能想要进行128字节对齐以便在出现时轻松转移到AVX-1024;)