c - 如何优化 omp pragma 以在并行区域之间运行代码?
问题描述
我有这个需要用 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;
}
在 Funct 中,Section 1、2、3 必须依次执行;第 2 节是严格顺序的。
在原始代码中,我被迫在函数 Funct(...) 中使用并行化,因此,可悲的是,创建线程的成本乘以迭代次数,但这不是问题,因为它仍然允许当 for inside main 或 vec 长度出现时进行一些时间优化(如果您有建议,我很乐意倾听)。问题是“第 2 节”,我认为实际上它使 OMP 产生了障碍或等待,但这会减慢执行速度;如果我删除该部分,我会得到一个相当可接受的优化,尊重顺序代码;可悲的是我不能。我尝试过 omp single、omp critical 等,看看它是否会将代码分配给先前池的某些线程,但是没有,有没有办法提高性能?(就像彻底改变编译指示,不是问题)
(使用 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();
解决方案
在平行部分的末端有一个隐含的障碍。改进代码的一种方法是将所有函数包含在一个#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
函数中,以便线程只产生一次。
推荐阅读
- sharepoint - sharepoint webpart xlsx 要列出
- logical-operators - 布尔代数:如何证明这个范式方程?
- python - 双向链表的反向实现是如何工作的?
- python - sqlalchemy-flask 应用程序中的属性错误(键),无法连接到数据库
- php - laravel 项目如何在没有路由和控制器的情况下工作?
- php - 如何通过 php 在 mysql 中使用 bcrypt
- mysql - 如何在 MySQL 中连接两个不相关的表?
- python - 如何阅读熊猫的第一列和最后一列?
- c++ - 警告处理为错误这里有什么问题?
- javascript - js如何知道子元素的个数?