问题引入
我在写代码的过程中发现使用OpenMP后反而运行速度更慢了,后来查了许多资料发现一些其他的使用陷阱,总结了这篇博文
问题一:临界变量
#include <omp.h>
#include <stdio.h>
int main(){
int sum=0;
#pragma omp parallel for num_threads(6)
for(int i=0;i<100;i++){
sum+=i;
}
printf("%d\n",sum);
}
输出:
2892
为什么不是4950呢,因为sum是临界变量,当多个线程同时对sum写时会相互覆盖。
解决方案
(1)在临界区添加#pragma omp critical
#include <omp.h>
#include <stdio.h>
int main(){
int sum=0;
#pragma omp parallel for num_threads(6)
for(int i=0;i<100;i++){
#pragma omp critical
{
sum+=i;
}
}
printf("%d\n",sum);
}
(2)在#pragma omp parallel指令中添加reduction子句,并指定要进行归约的操作和变量。
#include <omp.h>
#include <stdio.h>
int main(){
int sum=0;
#pragma omp parallel for num_threads(6) reduction(+:sum)
for(int i=0;i<100;i++){
sum+=i;
}
printf("%d\n",sum);
}
在这个例子中,reduction子句指定了对变量sum进行加法归约操作。
归约操作就是指满足结合律和交换律的操作例如
+ - * & | ^ max min
都是归约操作。
对于只是归约操作而言方案(2)相对来说更好,方案(1)临界区定义会使得只有一个线程进入临界区,其他线程等待,减慢了速度。
但是对于其他非归约操作,访问临界区需要方案(1)来解决。
问题二:变慢陷阱
int i,j;
#pragma omp parallel for
for(i = 0; i < divs*divs; ++i){
data d;
d.shallow = 0;
d.w = orig.w/divs * size;
d.h = orig.h/divs * size;
d.X.rows = orig.X.rows;
d.X.cols = d.w*d.h*3;
d.X.vals = calloc(d.X.rows, sizeof(float*));
d.y = copy_matrix(orig.y);
#pragma omp parallel for
for(j = 0; j < orig.X.rows; ++j){
int x = (i%divs) * orig.w / divs - (d.w - orig.w/divs)/2;
int y = (i/divs) * orig.h / divs - (d.h - orig.h/divs)/2;
image im = float_to_image(orig.w, orig.h, 3, orig.X.vals[j]);
d.X.vals[j] = crop_image(im, x, y, d.w, d.h).data;
}
ds[i] = d;
}
虽然全部核心都被使用但是速度反而比单核时慢,因为提前定义了i和j
解决方案
#pragma omp parallel for
for(int i = 0; i < divs*divs; ++i){
data d;
d.shallow = 0;
d.w = orig.w/divs * size;
d.h = orig.h/divs * size;
d.X.rows = orig.X.rows;
d.X.cols = d.w*d.h*3;
d.X.vals = calloc(d.X.rows, sizeof(float*));
d.y = copy_matrix(orig.y);
#pragma omp parallel for
for(int j = 0; j < orig.X.rows; ++j){
int x = (i%divs) * orig.w / divs - (d.w - orig.w/divs)/2;
int y = (i/divs) * orig.h / divs - (d.h - orig.h/divs)/2;
image im = float_to_image(orig.w, orig.h, 3, orig.X.vals[j]);
d.X.vals[j] = crop_image(im, x, y, d.w, d.h).data;
}
ds[i] = d;
}