5.1OpenMP
5.1.1OpenMP的介绍
OpenMP三个原则
5.2OpenMP的使用
编译制导指令以#pragma omp 开始,后边跟具体的功能指令,格式如:#pragma omp 指令[子句[,子句].]。常用的功能指令如下:
5.2.1编译制导
5.2.2API和环境变量
具体案例-邻接矩阵
// 本代码用于将图的邻接矩阵转换为包含边的二维矩阵,其中每一行是一个(i, j)对,表示从顶点i到顶点j的有向边#include <omp.h>
#include <cstdlib>
#include <cstdio>// 找到线程分配的行范围
// 参数:
// n: 矩阵的行数
// nth: 线程总数
// me: 当前线程编号
// myrange: 输出,表示当前线程负责的行范围
void findmyrange(int n,int nth,int me,int *myrange)
{int chunksize = n / nth; // 每个线程分配的行数myrange[0] = me * chunksize; // 起始行if (me < nth-1)myrange[1] = (me+1) * chunksize - 1; // 非最后一个线程的结束行elsemyrange[1] = n - 1; // 最后一个线程的结束行
}// 将邻接矩阵转换为包含边的二维矩阵
// 参数:
// adjm: 邻接矩阵,1表示有边,0表示无边,函数会覆盖矩阵内容
// n: 矩阵的行列数
// nout: 输出,返回的二维矩阵的行数
// 返回值: 指向转换后二维矩阵的指针
int *transgraph(int *adjm, int n, int *nout)
{int *outm; // 输出的二维矩阵int *num1s; // 记录邻接矩阵每一行中1的个数int *cumul1s; // 累计和数组,用于确定输出矩阵的起始位置#pragma omp parallel{int i, j, m;int me = omp_get_thread_num(); // 当前线程编号int nth = omp_get_num_threads(); // 总线程数int myrows[2]; // 当前线程负责的行范围int tot1s; // 每一行中1的个数int outrow, num1si; // 输出矩阵的当前行索引和1的个数#pragma omp single{num1s = (int*)malloc(n * sizeof(int)); // 分配记录1个数的数组cumul1s = (int*)malloc((n+1) * sizeof(int)); // 分配累计和数组}// 确定当前线程负责的行范围findmyrange(n, nth, me, myrows);// 遍历当前线程负责的行,记录每行中1的位置for (i = myrows[0]; i <= myrows[1]; i++) {tot1s = 0; // 当前行中1的个数for (j = 0; j < n; j++) {if (adjm[n*i+j] == 1) {adjm[n*i+(tot1s++)] = j; // 将1所在的列号写回矩阵}}num1s[i] = tot1s; // 记录当前行中1的个数}#pragma omp barrier // 确保所有线程都完成#pragma omp single{cumul1s[0] = 0; // 初始化累计和// 计算累计和数组for (m = 1; m <= n; m++) {cumul1s[m] = cumul1s[m-1] + num1s[m-1];}*nout = cumul1s[n]; // 输出矩阵的总行数outm = (int*)malloc(2 * (*nout) * sizeof(int)); // 分配输出矩阵}// 填充输出矩阵for (i = myrows[0]; i <= myrows[1]; i++) {outrow = cumul1s[i]; // 当前行在输出矩阵中的起始位置num1si = num1s[i]; // 当前行中1的个数for (j = 0; j < num1si; j++) {outm[2*(outrow+j)] = i; // 记录起点顶点outm[2*(outrow+j)+1] = adjm[n*i+j]; // 记录终点顶点}}}// 隐含的并行块结束同步return outm; // 返回输出矩阵
}