首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >SSE/AVX + OpenMP:快速数组求和

SSE/AVX + OpenMP:快速数组求和
EN

Stack Overflow用户
提问于 2013-03-07 20:54:26
回答 1查看 2.9K关注 0票数 2

我将两个数组相加,然后输出第三个数组(不是约简)。如下所示:

代码语言:javascript
复制
void add_scalar(float* result, const float* a, const float* b, const int N) {   
    for(int i = 0; i<N; i++) {
        result[i] = a[i] + b[i];
    }
}

我想用最大的吞吐量来做这件事。对于SSE和四核,我天真地期望速度提高16倍( SSE是四倍,四核是四倍)。我已经用SSE (和AVX)实现了代码。Visual studio 2012具有自动矢量化功能,但我通过“展开循环”获得了更好的结果。我针对四种大小的数组( 32字节对齐)运行代码:小于32KB、小于256KB、小于8MB和大于8MB的核分别对应于L1、L2、L3缓存和主存。对于L1,我看到使用我的展开的SSE代码的速度提高了4倍(使用AVX时为5-6倍)。这就是我所期望的。在此之后,每个高速缓存级别的效率都会下降。然后我使用OpenMP在每个内核上运行。我在数组上的主循环之前放置了“#杂注omp并行for”。然而,我得到的最好的加速比是SSE + OpenMP的5-6倍。有没有人知道为什么我没有看到16倍的加速?也许是由于数组从系统内存到缓存的一些“上载”时间?我意识到我应该分析代码,但这本身就是我必须学习的另一个冒险。

代码语言:javascript
复制
#define ROUND_DOWN(x, s) ((x) & ~((s)-1))  
void add_vector(float* result, const float* a, const float* b, const int N) {
    __m128 a4;
    __m128 b4;
    __m128 sum;
    int i = 0;
    for(; i < ROUND_DOWN(N, 8); i+=8) {
        a4 = _mm_load_ps(a + i);
        b4 = _mm_load_ps(b + i);
        sum = _mm_add_ps(a4, b4);
        _mm_store_ps(result + i, sum);
        a4 = _mm_load_ps(a + i + 4);
        b4 = _mm_load_ps(b + i + 4);
        sum = _mm_add_ps(a4, b4);
        _mm_store_ps(result + i + 4, sum);
    }
    for(; i < N; i++) {
        result[i] = a[i] + b[i];
    }
    return 0;
}

我的错误主循环具有类似如下的竞争条件:

代码语言:javascript
复制
float *a = (float*)_aligned_malloc(N*sizeof(float), 32);
float *b = (float*)_aligned_malloc(N*sizeof(float), 32);
float *c = (float*)_aligned_malloc(N*sizeof(float), 32);
#pragma omp parallel for
for(int n=0; n<M; n++) {  //M is an integer of the number of times to run over the array
    add_vector(c, a, b, N);
}

我根据Grizzly的建议修正了主循环:

代码语言:javascript
复制
for(int i=0; i<4; i++) {
    results[i] = (float*)_aligned_malloc(N*sizeof(float), 32);
}
#pragma omp parallel for num_threads(4)
for(int t=0; t<4; t++) {
    for(int n=0; n<M/4; n++) { //M is an integer of the number of times to run over the array
        add_vector(results[t], a, b, N);
    }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2013-03-07 21:56:29

免责声明:像你一样,我没有分析代码,所以我不能绝对肯定地回答。

您的问题很可能与内存带宽或并行化开销有关。

你的循环的计算量非常小,因为它为3次内存操作做了1次加法,这使得你自然受到内存带宽的限制(考虑到在现代体系结构中,ALU的吞吐量比内存带宽要好得多)。因此,您的大部分时间都花在传输数据上。

如果数据足够小,你可以(理论上)将openmp线程绑定到特定的内核,并确保向量的正确部分在特定内核的L1/L2缓存中,但这不会有真正的帮助,除非你可以并行化初始化(当你传输数据时,如果你无论如何都要这样做,这并不重要)。因此,将数据从一个核心缓存转移到另一个核心缓存会产生负面影响。

如果数据不适合处理器缓存,那么最终会受到主存带宽的限制。

要记住的第二点是,创建omp parallel构造并分发循环会产生一定的开销。对于小型数据集(适合L1/L2/L3的数据集可能符合条件),此开销很容易与计算时间本身一样高,几乎没有加速。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15271800

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档