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

Haa*_*hii 1 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];
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Goz*_*Goz 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];
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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