我正在尝试在函数
中并行化以下序列代码 float arr[100];
for ( int i = 0; i < 100; ++i) arr[i]=0;
int i,j;
for ( i = 0; i < 100; ++i) {
float* pi = arr+i;
int index[10];
f(i, index); // f fills in the array index
for (j = 0; j < 10; ++j) {
float* pj = arr+index[j];
if (pj > pi){
float tmp = g(i,j); // some function g
*pi += tmp;
*pj += tmp;
}
}
}
和类似的一个出现在另一个函数
中 float arr[100];
for ( int i = 0; i < 100; ++i) arr[i]=0;
int i,j;
for ( i = 0; i < 100; ++i) {
float* pi = arr+i;
int index[10];
f(i, index); // f fills in the array index
for (j = 0; j < 10; ++j) {
float* pj = arr+index[j];
if (pj > pi){
h(pi,i); // function h updates the memory pointed by pi,
// by adding to to *pi something, which only depends on i but not on the values pointed by pi.
h(pj,j); //
}
}
}
我的方式是:
float arr[100];
for ( int i = 0; i < 100; ++i) arr[i]=0;
int i,j;
#pragma omp parallel for shared(arr) private(i,j) schedule(auto)
for ( i = 0; i < 100; ++i) {
float* pi = arr+i;
int index[10];
f(i, index);
for (j = 0; j < 10; ++j) {
float* pj = arr+index[j];
if (pj > pi){
float tmp = g(i,j);
*pi += tmp;
#pragma omp atomic update
*pj += tmp;
}
}
}
和
float arr[100];
for ( int i = 0; i < 100; ++i) arr[i]=0;
int i,j;
#pragma omp parallel for shared(arr) private(i,j) schedule(auto)
for ( i = 0; i < 100; ++i) {
float* pi = arr+i;
int index[10];
f(i, index);
for (j = 0; j < 10; ++j) {
float* pj = arr+index[j];
if (pj > pi){
h(pi,i);
#pragma omp critical (pj)
h(pj,j); //
}
}
}
我使用原子和关键指令,因为多个线程可以同时写入pj指向的内存。 (如果我是正确的,线程不会同时写入pi指向的内存。)
但是,添加原子和关键指令会使运行时间与串行代码的运行时间大致相同。
所以我想知道该怎么办?
答案 0 :(得分:1)
并行化此代码的整个想法是有缺陷的。没有足够的工作来证明启动和同步线程的开销是合理的。
使用原子更新,同步本身的开销也不算太差,但关键部分将破坏所有性能:一次只能有一个线程进入,并且典型的开销时间为同步大约为微秒(取决于等待的实现方式)。这比使用一个核心的工作要差得多。
如果您完全设置并行化(我不打算在此处使用“优化”一词)调用h()
的循环,则应确保h()
在线程安全中运行方式(可能使用原子操作),忘掉#pragma omp critical
。