我有需要使用OpenMP优化的C代码,我无法编写原始代码,但这是一个替代品:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#ifdef _OPENMP
#include <omp.h>
#endif
void Funct(double *vec, int len)
{
int i;
double tmp;
//Section 1
#pragma omp parallel for
for ( i = 0; i < len; i++ ) //Code that initialize vec, it simulates an initialization in the original code
vec [ i ] = i;
//Section 2
//This code must be run sequentially
tmp = vec [ 0 ];
vec [0 ] = vec [ len - 1 ];
vec [ len - 1 ] = tmp;
tmp = vec [ 0 ];
vec [0 ] = vec [ len - 1 ];
vec [ len - 1 ] = tmp;
//End of the sequential code
//Section 3
#pragma omp parallel for
for ( i = 0; i < len; i++ ) //Code to simulate loadwork on vec
{
vec [ i ] = pow(vec[i], 2 );
vec [ i ] = sqrt ( vec [ i ] );
vec [ i ] += 1;
vec [ i ] = pow(vec[i], 2 );
vec [ i ] = sqrt ( vec [ i ] );
vec [ i ] -= 1;
}
}
int main ()
{
double *vec;
int i;
vec = (double *) malloc ( sizeof ( double ) * 5104 ); //Length of the vector in the original code
for ( i = 0; i < 1000000; i++ ) //Iteration in the original code
Funct(vec, 5104 );
for ( i = 0; i < 5; i++ ) // Access the array to avoid -O2 cancellations
printf ("%.2f ", vec [ i * 1000 ] );
return 0;
}
在功能中,第1、2和3节必须顺序执行;第2节严格按顺序进行。
在原始代码中,我被迫在函数Funct(...)内使用并行化,因此,可悲的是,创建线程的成本乘以迭代次数,但这不是问题,因为当main或vec长度内的for出现时,它仍然允许一些时间优化(如果您有建议,我很乐意倾听)。问题是“第2节”,事实上,我认为它使OMP产生了障碍或等待,但是这减慢了执行速度。如果删除该部分,我将获得相对于顺序代码而言相当不错的优化。可悲的是我不能。 我试过了omp single,ompcritical等,以查看是否将代码分配给了上一个缓冲池的某些线程,但是没有,有没有办法提高性能? (就像彻底改变实用程序一样,这不是问题)
(与gcc file.c -o file.out -lm -O2 -fopenmp一起编译,在Linux Lubuntu下使用time ./file.out进行了测试)
修改1: 我想指出
tmp = vec [ 0 ];
vec [0 ] = vec [ len - 1 ];
vec [ len - 1 ] = tmp;
tmp = vec [ 0 ];
vec [0 ] = vec [ len - 1 ];
vec [ len - 1 ] = tmp;
我只是在方法中放入了随机代码,以明确必须按顺序运行(它执行两次相同的操作,它交换vec [0]和vec [len-1],因此在执行没有真正发生) 我本来可以编写任何其他函数或代码;
例如我可以放
Foo1();
Foo2();
Foo3();
答案 0 :(得分:0)
将循环索引设置为
for ( i = 1; i < len-1; i++ )
,并将第一个和最后一个元素视为特殊情况。它们可以在OpenMP区域之外执行。
答案 1 :(得分:0)
在并行部分的末尾有一个隐式屏障。改进代码的一种方法是将所有功能都封装在#pragma omp parallel
指令中,以便在开始时仅产生一次线程,而在第1和3节中产生两次线程。
在omp for
循环的末尾,隐式屏障仍然存在,但是与生成新线程相比,其开销仍然较小。然后,必须将第2节括在omp single
块中(这很可能是您所做的,因为您提到 omp single 不能更好地工作,但是并不能100%清除) )。
void Funct(double *vec, int len)
{
// Create threads
#pragma omp parallel
{
//Section 1
#pragma omp for
for (int i = 0; i < len; i++ ){
//Code that initialize vec, it simulates an initialization in the original code
vec [ i ] = i;
} // Implicit barrier here (end of omp for loop)
//Section 2
//This code must be run sequentially
// It will start only once the section 1 has been completed
#pragma omp single
{
double tmp;
tmp = vec [ 0 ];
vec [0 ] = vec [ len - 1 ];
vec [ len - 1 ] = tmp;
tmp = vec [ 0 ];
vec [0 ] = vec [ len - 1 ];
vec [ len - 1 ] = tmp;
} // Implicit barrier here (end of omp single block)
//End of the sequential code
//Section 3
#pragma omp for
for ( i = 0; i < len; i++ ) //Code to simulate loadwork on vec
{
vec [ i ] = pow(vec[i], 2 );
vec [ i ] = sqrt ( vec [ i ] );
vec [ i ] += 1;
vec [ i ] = pow(vec[i], 2 );
vec [ i ] = sqrt ( vec [ i ] );
vec [ i ] -= 1;
} // Implicit barrier here end of for
} // Implicit barrier here end of parallel + destroy threads
}
最好是将omp parallel
指令移至main
函数,以便线程仅产生一次。