首页 >后端开发 >C++ >如何使用 SSE SIMD 指令在 Intel CPU 上开发快速前缀求和算法?

如何使用 SSE SIMD 指令在 Intel CPU 上开发快速前缀求和算法?

DDD
DDD原创
2024-11-27 11:52:09876浏览

How Can SSE SIMD Instructions Be Used to Develop a Fast Prefix Sum Algorithm on Intel CPUs?

Intel CPU 上的 SIMD 前缀和

问题:

使用 SSE SIMD CPU 开发快速前缀和算法

答案:

最佳解决方案涉及两个并行传递:

传递 1:

  • 使用 SSE 并行计算部分和SIMD。
  • 存储每个部分和的总和。

第 2 步:

  • 添加总和前一部分和下一部分和,使用SIMD。

优点:

  • 并行性减少了两个通道中的计算时间。
  • 第 2 通道中的 SIMD 优化进一步增强

实现说明:

  • 算法的时间成本估计为 (n/m)*(1 1/w) ,其中n是数组大小,m是核心数量,w是SIMD宽度。
  • 该算法显着比顺序实现更快,在四核系统上提供约 7 倍的加速系数。
  • 对于大型数组,第二遍可以通过按顺序分块和执行块来进一步优化,同时将数据保留在缓存中。

代码示例:

__m128 scan_SSE(__m128 x) {
    x = _mm_add_ps(x, _mm_castsi128_ps(_mm_slli_si128(_mm_castps_si128(x), 4)));
    x = _mm_add_ps(x, _mm_shuffle_ps(_mm_setzero_ps(), x, 0x40));
    return x;
}

float pass1_SSE(float *a, float *s, const int n) {
    __m128 offset = _mm_setzero_ps();
    #pragma omp for schedule(static) nowait
    for (int i = 0; i < n / 4; i++) {
        __m128 x = _mm_load_ps(&a[4 * i]);
        __m128 out = scan_SSE(x);
        out = _mm_add_ps(out, offset);
        _mm_store_ps(&s[4 * i], out);
        offset = _mm_shuffle_ps(out, out, _MM_SHUFFLE(3, 3, 3, 3));
    }
    float tmp[4];
    _mm_store_ps(tmp, offset);
    return tmp[3];
}

void pass2_SSE(float *s, __m128 offset, const int n) {
    #pragma omp for schedule(static)
    for (int i = 0; i<n/4; i++) {
        __m128 tmp1 = _mm_load_ps(&s[4 * i]);
        tmp1 = _mm_add_ps(tmp1, offset);
        _mm_store_ps(&s[4 * i], tmp1);
    }
}

void scan_omp_SSEp2_SSEp1_chunk(float a[], float s[], int n) {
    float *suma;
    const int chunk_size = 1<<18;
    const int nchunks = n%chunk_size == 0 ? n / chunk_size : n / chunk_size + 1;

    #pragma omp parallel
    {
        const int ithread = omp_get_thread_num();
        const int nthreads = omp_get_num_threads();

        #pragma omp single
        {
            suma = new float[nthreads + 1];
            suma[0] = 0;
        }

        float offset2 = 0.0f;
        for (int c = 0; c < nchunks; c++) {
            const int start = c*chunk_size;
            const int chunk = (c + 1)*chunk_size < n ? chunk_size : n - c*chunk_size;
            suma[ithread + 1] = pass1_SSE(&a[start], &s[start], chunk);
            #pragma omp barrier
            #pragma omp single
            {
                float tmp = 0;
                for (int i = 0; i < (nthreads + 1); i++) {
                    tmp += suma[i];
                    suma[i] = tmp;
                }
            }
            __m128 offset = _mm_set1_ps(suma[ithread]+offset2);
            pass2_SSE(&s[start], offset, chunk);
            #pragma omp barrier
            offset2 = s[start + chunk-1];
        }
    }
    delete[] suma;
}

以上是如何使用 SSE SIMD 指令在 Intel CPU 上开发快速前缀求和算法?的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn