gpt4 book ai didi

c++ - 即使在 num_threads(1) 时,openmp 的性能提升也难以理解

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:28:58 26 4
gpt4 key购买 nike

下面几行代码

int nrows = 4096;
int ncols = 4096;
size_t numel = nrows * ncols;
unsigned char *buff = (unsigned char *) malloc( numel );

unsigned char *pbuff = buff;
#pragma omp parallel for schedule(static), firstprivate(pbuff, nrows, ncols), num_threads(1)
for (int i=0; i<nrows; i++)
{
for (int j=0; j<ncols; j++)
{
*pbuff += 1;
pbuff++;
}
}

编译时需要 11130 usecs 在我的 i5-3230M 上运行

g++ -o main main.cpp -std=c++0x -O3

也就是说,当 openmp pragma 被忽略时。

另一方面,编译时只需要 1496 usecs

g++ -o main main.cpp -std=c++0x -O3 -fopenmp

速度快了 6 倍多,考虑到它是在 2 核机器上运行,这是相当令人惊讶的。事实上,我也用 num_threads(1) 测试过,性能提升还是很重要的(快了 3 倍多)。

任何人都可以帮助我理解这种行为吗?

编辑:按照建议,我提供了完整的代码:

#include <stdlib.h>
#include <iostream>

#include <chrono>
#include <cassert>


int nrows = 4096;
int ncols = 4096;
size_t numel = nrows * ncols;
unsigned char * buff;


void func()
{
unsigned char *pbuff = buff;
#pragma omp parallel for schedule(static), firstprivate(pbuff, nrows, ncols), num_threads(1)
for (int i=0; i<nrows; i++)
{
for (int j=0; j<ncols; j++)
{
*pbuff += 1;
pbuff++;
}
}
}


int main()
{
// alloc & initializacion
buff = (unsigned char *) malloc( numel );
assert(buff != NULL);
for(int k=0; k<numel; k++)
buff[k] = 0;

//
std::chrono::high_resolution_clock::time_point begin;
std::chrono::high_resolution_clock::time_point end;
begin = std::chrono::high_resolution_clock::now();
//
for(int k=0; k<100; k++)
func();
//
end = std::chrono::high_resolution_clock::now();
auto usec = std::chrono::duration_cast<std::chrono::microseconds>(end-begin).count();
std::cout << "func average running time: " << usec/100 << " usecs" << std::endl;

return 0;
}

最佳答案

事实证明,答案是 firstprivate(pbuff, nrows, ncols) 有效地声明了 pbuffnrowsncols 作为 for 循环范围内的局部变量。这反过来意味着编译器可以将 nrowsncols 视为常量 - 它不能对全局变量做出相同的假设!

因此,使用 -fopenmp,您最终会获得巨大的加速,因为您不会在每次迭代中访问全局变量。 (另外,使用常量 ncols 值,编译器会执行一些循环展开操作)。

通过改变

int nrows = 4096;
int ncols = 4096;

const int nrows = 4096;
const int ncols = 4096;

通过改变

for (int i=0; i<nrows; i++)
{
for (int j=0; j<ncols; j++)
{
*pbuff += 1;
pbuff++;
}
}

int _nrows = nrows;
int _ncols = ncols;
for (int i=0; i<_nrows; i++)
{
for (int j=0; j<_ncols; j++)
{
*pbuff += 1;
pbuff++;
}
}

异常加速消失了 - 非 OpenMP 代码现在与 OpenMP 代码一样快。

故事的寓意?避免在性能关键循环中访问可变全局变量。

关于c++ - 即使在 num_threads(1) 时,openmp 的性能提升也难以理解,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30656012/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com