1 环境配置与安装
vs下的openmp只需去项目->属性->c/c++->openmp支持打开即可
2 mpi与openmp混合编程
混合编程首先要对两种都有一定的了解
openmp是基于共享内存下的并行操作,采用openmp执行并行的程序优点在于可以不用给所有线程都给予它自己的内存空间来存储变量(但是请注意如果多个线程反复读写同一块区域会造成排队现象而大幅度的减少并行效率,有时甚至可能更慢),也正因为openmp程序是共享内存,自然可以得知openmp是不能在多cpu多主机上跑的。
而mpi则是进程级别的并行,每个进程都需要自己独立的内存空间,进程之间通过mpi提供的接口来进行信息的交换与发送。显然基于这一特点可以明白,mpi虽然可以在多主机,多cpu,甚至多gpu上进行并行,但是对于内存的占用量是很大的。
而在结合了上述两点自然而然就会想到运用mpi与openmp的混合编程来同时获取两者的优点,但请注意openmp+MPI混合编程在MPI基础上加大了复杂度,采用它的目的,是减少内存占用,而非提高性能。即使是在单个节点,纯粹的共享内存系统,MPI程序在性能上也不输openmp,大多数时候甚至更好。
3 一个简单的三维点阵求和程序
注释已经写的挺详细了,注意用mpiexec来运行,其中计算线程号的方法采用了树形的计算
计算方法也很简单,线程号就等于threadID+rankID*threadnum,其他有不懂得可以参考上一个博客,原程序是从那边改过来的
#include <iostream>
#include <mpi.h>
#include <omp.h>
#include <time.h>
#include <math.h>
#include <thread>
#define maxs 30
clock_t start, end = 0;
struct pointsSquare
{
double x, y, z;
};
int main(int argc, char* argv[])
{
MPI_Init(&argc, &argv);
//MPI并行分为数个rank
int Ranknum;
int RankID;
MPI_Comm_rank(MPI_COMM_WORLD, &RankID);
MPI_Comm_size(MPI_COMM_WORLD, &Ranknum);
//openmp将其并行为threadnum个线程,如果mpi给每个主机分配一个可以考虑直接设置为核心数
int threadnum = 3;//openmp的并行数
omp_set_num_threads(threadnum);//设定并行的线程数
int Tnum = threadnum * Ranknum;//共分为多少线程
pointsSquare ps[maxs + 1][maxs + 1][maxs + 1];//建立点阵,由于后面循环是从1开始需要多开一位
double sum[maxs];
for (int i = 1; i <= maxs; i++) {
for (int j = 1; j <= maxs; j++) {
for (int k = 1; k <= maxs; k++) {
ps[i][j][k].x = i;
ps[i][j][k].y = j;
ps[i][j][k].z = k;
}
}
}
start = clock();//开始计时
#pragma omp parallel
{
//每个循环独立内存防止访存冲突
double dTemp1, dTemp2, dTemp3;
double d1, d2, d3;
int iThread;
int iAddThread = 0;
int i, j, k, m, n, l;
iThread = omp_get_thread_num() + RankID*threadnum; //获取线程号
double sum_thread = 0.0;
for (i = 1; i <= maxs; i++) {
//iAddThread += 1;
//iAddThread = i;
//if (iAddThread % nThread == iThread) {
//if (iAddThread - nThread * (iAddThread / nThread) == iThread) {
//if (i- threadnum *(i/ threadnum) == iThread) {
for (j = 1; j <= maxs; j++) {
for (k = 1; k <= maxs; k++) {
if (i % Tnum == iThread) {
//上面是外层坐标循环,下面是该点与其他点的循环
for (m = 1; m <= maxs; m++) {
for (n = 1; n <= maxs; n++) {
for (l = 1; l <= maxs; l++) {
/*dTemp1 = (ps[i][j][k].x - ps[m][n][l].x);
dTemp2 = (ps[i][j][k].y - ps[m][n][l].y);
dTemp3 = (ps[i][j][k].z - ps[m][n][l].z);*/
dTemp1 = (ps[i][j][k].x - ps[m][n][l].x);
dTemp2 = (ps[i][j][k].y - ps[m][n][l].y);
dTemp3 = (ps[i][j][k].z - ps[m][n][l].z);
d1 = dTemp1 * dTemp1;
d2 = dTemp2 * dTemp2;
d3 = dTemp3 * dTemp3;
//d1 = pow(dTemp1, 2.0); //Pow不能用,速度慢10倍
//d2 = pow(dTemp2, 2.0);
//d3 = pow(dTemp3, 2.0);
//temp = sqrt(d1 + d2 + d3) * 10000 + 0.5;
//int temp1 = sqrt(d1 + d2 + d3) * 10000;
//int temp2 = sqrt(d1 + d2 + d3) * 10000;
//int temp3 = sqrt(d1 + d2 + d3) * 10000;
//sum[iThread] += sqrt(d1 + d2 + d3); //距离求和
//sum[iThread] += pow( (d1 + d2 + d3), 0.5); //距离求和 // 不能用,速度慢
sum_thread += sqrt(d1 + d2 + d3); //距离求和
//sum_thread += sqrt(1.0);
//sum[iThread] = sum[iThread]+ sqrt(1.0);
}
}
}
}
}
}
}
sum[iThread] = sum_thread;
}
double sum_rank = 0;
for(int i = RankID * threadnum; i < (RankID+1) * threadnum;i++ ) {
sum_rank += sum[i];
std::cout << sum[i] << std::endl;
}
double sumx = 0;
MPI_Reduce(&sum_rank, &sumx, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);//mpi的规约,将所有sumrank规约到进程0
if (RankID == 0) {
std::cout << threadnum << std::endl;
printf("Sum: %f\n", sumx);
end = clock();
printf("time=%f\n", (double)(end - start) / CLOCKS_PER_SEC);
}
MPI_Finalize();
return 0;
}