以下代码行
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++;
}
}
Run Code Online (Sandbox Code Playgroud)
在编译时使用11130 usecs在我的i5-3230M上运行
g++ -o main main.cpp -std=c++0x -O3
Run Code Online (Sandbox Code Playgroud)
也就是说,当openmp pragma被忽略时.
另一方面,编译时只需要1496个usecs
g++ -o main main.cpp -std=c++0x -O3 -fopenmp
Run Code Online (Sandbox Code Playgroud)
这速度提高了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;
}
Run Code Online (Sandbox Code Playgroud)
事实证明,答案是firstprivate(pbuff, nrows, ncols)有效地声明pbuff,nrows并ncols作为for循环范围内的局部变量.这反过来意味着编译器可以看到nrows和ncols作为常量 - 它不能对全局变量做出相同的假设!
因此,-fopenmp您最终会获得巨大的加速,因为您不会在每次迭代时访问全局变量.(另外,使用常ncols量值,编译器可以进行一些循环展开).
通过改变
int nrows = 4096;
int ncols = 4096;
Run Code Online (Sandbox Code Playgroud)
至
const int nrows = 4096;
const int ncols = 4096;
Run Code Online (Sandbox Code Playgroud)
或通过改变
for (int i=0; i<nrows; i++)
{
for (int j=0; j<ncols; j++)
{
*pbuff += 1;
pbuff++;
}
}
Run Code Online (Sandbox Code Playgroud)
至
int _nrows = nrows;
int _ncols = ncols;
for (int i=0; i<_nrows; i++)
{
for (int j=0; j<_ncols; j++)
{
*pbuff += 1;
pbuff++;
}
}
Run Code Online (Sandbox Code Playgroud)
异常加速消失 - 非OpenMP代码现在和OpenMP代码一样快.
这个故事的主旨?避免在性能关键循环中访问可变全局变量.
| 归档时间: |
|
| 查看次数: |
145 次 |
| 最近记录: |