▶ 《并行程序设计导论》第六章中讨论了 n 体问题,分别使用了 MPI,Pthreads,OpenMP 来进行实现,这里是 MPI 的代码,分为基本算法和简化算法(引力计算量为基本算法的一半,但是消息传递较为复杂)

● 基本算法

 //mpi_nbody_basic.c,MPI 基本算法
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <mpi.h> #define OUTPUT // 要求输出结果
#define DEBUG // debug 模式,每个函数给出更多的输出
#define DIM 2 // 二维系统
#define X 0 // X 坐标
#define Y 1 // Y 坐标
typedef double vect_t[DIM]; // 向量数据类型
const double G = 6.673e-11; // 万有引力常量
int my_rank, comm_sz; // 进程编号和总进程数
MPI_Datatype vect_mpi_t; // 使用的派生数据类型
vect_t *vel = NULL; // 全局颗粒速度,用于 0 号进程的输出 void Usage(char* prog_name)// 输入说明
{
fprintf(stderr, "usage: mpiexec -n <nProcesses> %s\n", prog_name);
fprintf(stderr, "<nParticle> <nTimestep> <sizeTimestep> <outputFrequency> <g|i>\n");
fprintf(stderr, " 'g': inite condition by random\n");
fprintf(stderr, " 'i': inite condition from stdin\n");
exit();
} void Get_args(int argc, char* argv[], int* n_p, int* n_steps_p, double* delta_t_p, int* output_freq_p, char* g_i_p)// 获取参数信息
{ // 所有进程均调用该函数,因为有集合通信,但只有 0 号进程处理参数
if (my_rank == )
{
if (argc != )
Usage(argv[]);
*n_p = strtol(argv[], NULL, );
*n_steps_p = strtol(argv[], NULL, );
*delta_t_p = strtod(argv[], NULL);
*output_freq_p = strtol(argv[], NULL, );
*g_i_p = argv[][];
if (*n_p <= || *n_p % comm_sz || *n_steps_p < || *delta_t_p <= || *g_i_p != 'g' && *g_i_p != 'i')// 不合要求的输入情况
{
printf("haha\n");
if (my_rank == )
Usage(argv[]);
MPI_Finalize();
exit();
}
}
MPI_Bcast(n_p, , MPI_INT, , MPI_COMM_WORLD);
MPI_Bcast(n_steps_p, , MPI_INT, , MPI_COMM_WORLD);
MPI_Bcast(delta_t_p, , MPI_DOUBLE, , MPI_COMM_WORLD);
MPI_Bcast(output_freq_p, , MPI_INT, , MPI_COMM_WORLD);
MPI_Bcast(g_i_p, , MPI_CHAR, , MPI_COMM_WORLD);
# ifdef DEBUG// 确认各进程中的参数情况
printf("Get_args, rank%2d n %d n_steps %d delta_t %e output_freq %d g_i %c\n",
my_rank, *n_p, *n_steps_p, *delta_t_p, *output_freq_p, *g_i_p);
fflush(stdout);
# endif
} void Gen_init_cond(double masses[], vect_t pos[], vect_t loc_vel[], int n, int loc_n)// 自动生成初始条件,所有进程均调用该函数,因为有集合通信
{ // 生成的颗粒位于原点和 X 正半轴,速度大小相等,方向平行于 Y 轴,交错向上下
const double mass = 5.0e24, gap = 1.0e5, speed = 3.0e4;// 使用了地球的质量和公转速度
if (my_rank == )
{
// srand(2);// 使用随机方向和速度大小,下同
for (int i = ; i < n; i++)
{
masses[i] = mass;
pos[i][X] = i * gap;
pos[i][Y] = 0.0;
vel[i][X] = 0.0;
// vel[i][Y] = speed * (2 * rand() / (double)RAND_MAX) - 1);
vel[i][Y] = (i % ) ? -speed : speed;
}
}
// 同步质量,位置信息,分发速度信息
MPI_Bcast(masses, n, MPI_DOUBLE, , MPI_COMM_WORLD);
MPI_Bcast(pos, n, vect_mpi_t, , MPI_COMM_WORLD);
MPI_Scatter(vel, loc_n, vect_mpi_t, loc_vel, loc_n, vect_mpi_t, , MPI_COMM_WORLD);
# ifdef DEBUG// 确认各进程中第一个颗粒的初始条件
printf("Gen_init_cond, rank%2d %10.3e %10.3e %10.3e %10.3e %10.3e\n",
my_rank, masses[], pos[][X], pos[][Y], loc_vel[][X], loc_vel[][Y]);
fflush(stdout);
# endif
} void Get_init_cond(double masses[], vect_t pos[], vect_t loc_vel[], int n, int loc_n)// 手工输入初始条件,类似函数 Gen_init_cond()
{
if (my_rank == )
{
printf("For each particle, enter (in order): mass x-coord y-coord x-velocity y-velocity\n");
for (int i = ; i < n; i++)
{
scanf_s("%lf", &masses[i]);
scanf_s("%lf", &pos[i][X]);
scanf_s("%lf", &pos[i][Y]);
scanf_s("%lf", &vel[i][X]);
scanf_s("%lf", &vel[i][Y]);
}
}
MPI_Bcast(masses, n, MPI_DOUBLE, , MPI_COMM_WORLD);
MPI_Bcast(pos, n, vect_mpi_t, , MPI_COMM_WORLD);
MPI_Scatter(vel, loc_n, vect_mpi_t, loc_vel, loc_n, vect_mpi_t, , MPI_COMM_WORLD);
# ifdef DEBUG
printf("Get_init_cond, rank%2d %10.3e %10.3e %10.3e %10.3e %10.3e\n",
my_rank, masses[], pos[][X], pos[][Y], loc_vel[][X], loc_vel[][Y]);
fflush(stdout);
# endif
} void Output_state(double time, double masses[], vect_t pos[], vect_t loc_vel[], int n, int loc_n)// 输出当前状态
{
MPI_Gather(loc_vel, loc_n, vect_mpi_t, vel, loc_n, vect_mpi_t, , MPI_COMM_WORLD);// 从各进程聚集速度信息用于输出
if (my_rank == )
{
printf("Output_state, time = %.2f\n", time);
for (int i = ; i < n; i++)
printf(" %2d %10.3e %10.3e %10.3e %10.3e %10.3e\n", i, masses[i], pos[i][X], pos[i][Y], vel[i][X], vel[i][Y]);
printf("\n");
fflush(stdout);
}
} void Compute_force(int loc_part, double masses[], vect_t loc_forces[], vect_t pos[], int n, int loc_n)// 计算颗粒 part 受到的万有引力
{
const int part = my_rank * loc_n + loc_part;// 将局部颗粒编号转化为全局颗粒编号
int k;
vect_t f_part_k;
double len, fact;
for (loc_forces[loc_part][X] = loc_forces[loc_part][Y] = 0.0, k = ; k < n; k++)
{
if (k != part)
{
f_part_k[X] = pos[part][X] - pos[k][X];
f_part_k[Y] = pos[part][Y] - pos[k][Y];
len = sqrt(f_part_k[X] * f_part_k[X] + f_part_k[Y] * f_part_k[Y]);
fact = -G * masses[part] * masses[k] / (len * len * len);
f_part_k[X] *= fact;
f_part_k[Y] *= fact;
loc_forces[loc_part][X] += f_part_k[X];
loc_forces[loc_part][Y] += f_part_k[Y];
# ifdef DEBUG// 确认计算结果
printf("Compute_force, rank%2d k%2d> %10.3e %10.3e %10.3e %10.3e\n", my_rank, k, len, fact, f_part_k[X], f_part_k[Y]);
fflush(stdout);// 函数 printf() 中引用 vel 会导致非 0 号进程中断退出,吃了大亏
# endif
}
}
} void Update_part(int loc_part, double masses[], vect_t loc_forces[], vect_t loc_pos[], vect_t loc_vel[], int n, int loc_n, double delta_t)// 更新颗粒位置
{
const int part = my_rank * loc_n + loc_part;
const double fact = delta_t / masses[part];
# ifdef DEBUG// 输出在更新前和更新后的数据
printf("Update_part before, part%2d %10.3e %10.3e %10.3e %10.3e %10.3e %10.3e\n",
part, loc_pos[loc_part][X], loc_pos[loc_part][Y], loc_vel[loc_part][X], loc_vel[loc_part][Y], loc_forces[loc_part][X], loc_forces[loc_part][Y]);
fflush(stdout);
# endif
loc_pos[loc_part][X] += delta_t * loc_vel[loc_part][X];
loc_pos[loc_part][Y] += delta_t * loc_vel[loc_part][Y];
loc_vel[loc_part][X] += fact * loc_forces[loc_part][X];
loc_vel[loc_part][Y] += fact * loc_forces[loc_part][Y];
# ifdef DEBUG
printf("Update_part after, part%2d %10.3e %10.3e %10.3e %10.3e\n",
part, loc_pos[loc_part][X], loc_pos[loc_part][Y], loc_vel[loc_part][X], loc_vel[loc_part][Y]);
fflush(stdout);
# endif
} int main(int argc, char* argv[])
{
int n, loc_n, loc_part; // 颗粒数,每进程颗粒数,当前颗粒(循环变量)
int n_steps, step; // 计算时间步数,当前时间片(循环变量)
double delta_t; // 计算时间步长
int output_freq; // 数据输出频率
double *masses; // 颗粒质量,每个进程都有,一经初始化和同步就不再改变
vect_t *pos, *loc_pos; // 颗粒位置,每个时间片计算完成后需要同步
vect_t *loc_vel; // 颗粒速度,由各进程分开保存,不到输出时不用同步
vect_t *loc_forces; // 各进程的颗粒所受引力
char g_i; // 初始条件选项,g 为自动生成,i 为手工输入
double start, finish; // 计时器 MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &comm_sz);
MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
MPI_Type_contiguous(DIM, MPI_DOUBLE, &vect_mpi_t);// 提交需要的派生类型
MPI_Type_commit(&vect_mpi_t); // 获取参数,初始化数组
Get_args(argc, argv, &n, &n_steps, &delta_t, &output_freq, &g_i);
loc_n = n / comm_sz; // 要求 n % comm_sz == 0
masses = (double*)malloc(n * sizeof(double));
pos = (vect_t*)malloc(n * sizeof(vect_t));
loc_pos = pos + my_rank * loc_n;
loc_vel = (vect_t*)malloc(loc_n * sizeof(vect_t));
loc_forces = (vect_t*)malloc(loc_n * sizeof(vect_t));
if (my_rank == )
vel = (vect_t*)malloc(n * sizeof(vect_t));
if (g_i == 'g')
Gen_init_cond(masses, pos, loc_vel, n, loc_n);
else
Get_init_cond(masses, pos, loc_vel, n, loc_n); // 开始计算并计时
if (my_rank == )
start = MPI_Wtime();
# ifdef OUTPUT// 输出出初始态
Output_state(0.0, masses, pos, loc_vel, n, loc_n);
# endif
for (step = ; step <= n_steps; step++)
{
// 计算每颗粒受力,更新颗粒状态,然后同步颗粒位置
for (loc_part = ; loc_part < loc_n; Compute_force(loc_part++, masses, loc_forces, pos, n, loc_n));
for (loc_part = ; loc_part < loc_n; Update_part(loc_part++, masses, loc_forces, loc_pos, loc_vel, n, loc_n, delta_t));
MPI_Allgather(MPI_IN_PLACE, loc_n, vect_mpi_t, pos, loc_n, vect_mpi_t, MPI_COMM_WORLD);
# ifdef OUTPUT// 每隔一个输出时间间隔就输出一次
if (step % output_freq == )
Output_state(step * delta_t, masses, pos, loc_vel, n, loc_n);
# endif
}
// 报告计时,释放资源
if (my_rank == )
{
finish = MPI_Wtime();
printf("Elapsed time = %e ms\n", (finish - start) * );
free(vel);
}
MPI_Type_free(&vect_mpi_t);
free(masses);
free(pos);
free(loc_vel);
free(loc_forces);
MPI_Finalize();
return ;
}

● 输出结果。8 进程 16 体,3 秒,时间步长 1 秒,舍去 debug 输出 1.264884e+00 ms;8 进程 1024 体,3600 秒,时间步长 1 秒,舍去 output 和 debug 输出 1.328894e+04 ms

D:\Code\MPI\MPIProjectTemp\x64\Debug>mpiexec -n  MPIProjectTemp.exe     g
Output_state, time = 0.00
5.000e+24 0.000e+00 0.000e+00 0.000e+00 3.000e+04
5.000e+24 1.000e+05 0.000e+00 0.000e+00 -3.000e+04
5.000e+24 2.000e+05 0.000e+00 0.000e+00 3.000e+04
5.000e+24 3.000e+05 0.000e+00 0.000e+00 -3.000e+04
5.000e+24 4.000e+05 0.000e+00 0.000e+00 3.000e+04
5.000e+24 5.000e+05 0.000e+00 0.000e+00 -3.000e+04
5.000e+24 6.000e+05 0.000e+00 0.000e+00 3.000e+04
5.000e+24 7.000e+05 0.000e+00 0.000e+00 -3.000e+04
5.000e+24 8.000e+05 0.000e+00 0.000e+00 3.000e+04
5.000e+24 9.000e+05 0.000e+00 0.000e+00 -3.000e+04
5.000e+24 1.000e+06 0.000e+00 0.000e+00 3.000e+04
5.000e+24 1.100e+06 0.000e+00 0.000e+00 -3.000e+04
5.000e+24 1.200e+06 0.000e+00 0.000e+00 3.000e+04
5.000e+24 1.300e+06 0.000e+00 0.000e+00 -3.000e+04
5.000e+24 1.400e+06 0.000e+00 0.000e+00 3.000e+04
5.000e+24 1.500e+06 0.000e+00 0.000e+00 -3.000e+04 Output_state, time = 1.00
5.000e+24 0.000e+00 3.000e+04 5.273e+04 3.000e+04
5.000e+24 1.000e+05 -3.000e+04 1.922e+04 -3.000e+04
5.000e+24 2.000e+05 3.000e+04 1.071e+04 3.000e+04
5.000e+24 3.000e+05 -3.000e+04 6.802e+03 -3.000e+04
5.000e+24 4.000e+05 3.000e+04 4.485e+03 3.000e+04
5.000e+24 5.000e+05 -3.000e+04 2.875e+03 -3.000e+04
5.000e+24 6.000e+05 3.000e+04 1.614e+03 3.000e+04
5.000e+24 7.000e+05 -3.000e+04 5.213e+02 -3.000e+04
5.000e+24 8.000e+05 3.000e+04 -5.213e+02 3.000e+04
5.000e+24 9.000e+05 -3.000e+04 -1.614e+03 -3.000e+04
5.000e+24 1.000e+06 3.000e+04 -2.875e+03 3.000e+04
5.000e+24 1.100e+06 -3.000e+04 -4.485e+03 -3.000e+04
5.000e+24 1.200e+06 3.000e+04 -6.802e+03 3.000e+04
5.000e+24 1.300e+06 -3.000e+04 -1.071e+04 -3.000e+04
5.000e+24 1.400e+06 3.000e+04 -1.922e+04 3.000e+04
5.000e+24 1.500e+06 -3.000e+04 -5.273e+04 -3.000e+04 Output_state, time = 2.00
5.000e+24 5.273e+04 6.000e+04 9.288e+04 1.641e+04
5.000e+24 1.192e+05 -6.000e+04 3.818e+04 -3.791e+03
5.000e+24 2.107e+05 6.000e+04 2.116e+04 3.791e+03
5.000e+24 3.068e+05 -6.000e+04 1.356e+04 -3.101e+03
5.000e+24 4.045e+05 6.000e+04 8.930e+03 3.101e+03
5.000e+24 5.029e+05 -6.000e+04 5.739e+03 -2.959e+03
5.000e+24 6.016e+05 6.000e+04 3.218e+03 2.959e+03
5.000e+24 7.005e+05 -6.000e+04 1.043e+03 -2.929e+03
5.000e+24 7.995e+05 6.000e+04 -1.043e+03 2.929e+03
5.000e+24 8.984e+05 -6.000e+04 -3.218e+03 -2.959e+03
5.000e+24 9.971e+05 6.000e+04 -5.739e+03 2.959e+03
5.000e+24 1.096e+06 -6.000e+04 -8.930e+03 -3.101e+03
5.000e+24 1.193e+06 6.000e+04 -1.356e+04 3.101e+03
5.000e+24 1.289e+06 -6.000e+04 -2.116e+04 -3.791e+03
5.000e+24 1.381e+06 6.000e+04 -3.818e+04 3.791e+03
5.000e+24 1.447e+06 -6.000e+04 -9.288e+04 -1.641e+04 Output_state, time = 3.00
5.000e+24 1.456e+05 7.641e+04 1.273e+05 -1.575e+03
5.000e+24 1.574e+05 -6.379e+04 5.867e+04 2.528e+04
5.000e+24 2.319e+05 6.379e+04 2.685e+04 -2.069e+04
5.000e+24 3.204e+05 -6.310e+04 1.884e+04 2.228e+04
5.000e+24 4.134e+05 6.310e+04 1.235e+04 -2.151e+04
5.000e+24 5.086e+05 -6.296e+04 8.111e+03 2.179e+04
5.000e+24 6.048e+05 6.296e+04 4.537e+03 -2.163e+04
5.000e+24 7.016e+05 -6.293e+04 1.493e+03 2.169e+04
5.000e+24 7.984e+05 6.293e+04 -1.493e+03 -2.169e+04
5.000e+24 8.952e+05 -6.296e+04 -4.537e+03 2.163e+04
5.000e+24 9.914e+05 6.296e+04 -8.111e+03 -2.179e+04
5.000e+24 1.087e+06 -6.310e+04 -1.235e+04 2.151e+04
5.000e+24 1.180e+06 6.310e+04 -1.884e+04 -2.228e+04
5.000e+24 1.268e+06 -6.379e+04 -2.685e+04 2.069e+04
5.000e+24 1.343e+06 6.379e+04 -5.867e+04 -2.528e+04
5.000e+24 1.354e+06 -7.641e+04 -1.273e+05 1.575e+03 Elapsed time = 1.264884e+00 ms

● 简化算法

 // mpi_nbody_red.c,MPI 简化算法,与 mppi_nbody_basic.c 相同的地方去掉了注释
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <mpi.h> #define OUTPUT
#define DEBUG
#define DIM 2
#define X 0
#define Y 1
typedef double vect_t[DIM];
const double G = 6.673e-11;
int my_rank, comm_sz;
MPI_Datatype vect_mpi_t;
MPI_Datatype cyclic_mpi_t; // 环形传输的数据类型
vect_t *vel = NULL;
vect_t *pos = NULL; // 位置信息变成了全局变量 void Usage(char* prog_name)
{
fprintf(stderr, "usage: mpiexec -n <nProcesses> %s\n", prog_name);
fprintf(stderr, "<nParticle> <nTimestep> <sizeTimestep> <outputFrequency> <g|i>\n");
fprintf(stderr, " 'g': inite condition by random\n");
fprintf(stderr, " 'i': inite condition from stdin\n");
exit();
} void Get_args(int argc, char* argv[], int* n_p, int* n_steps_p, double* delta_t_p, int* output_freq_p, char* g_i_p)
{
if (my_rank == )
{
if (argc != )
Usage(argv[]);
*n_p = strtol(argv[], NULL, );
*n_steps_p = strtol(argv[], NULL, );
*delta_t_p = strtod(argv[], NULL);
*output_freq_p = strtol(argv[], NULL, );
*g_i_p = argv[][];
if (*n_p <= || *n_p % comm_sz || *n_steps_p < || *delta_t_p <= || *g_i_p != 'g' && *g_i_p != 'i')
{
printf("haha\n");
if (my_rank == )
Usage(argv[]);
MPI_Finalize();
exit();
}
}
MPI_Bcast(n_p, , MPI_INT, , MPI_COMM_WORLD);
MPI_Bcast(n_steps_p, , MPI_INT, , MPI_COMM_WORLD);
MPI_Bcast(delta_t_p, , MPI_DOUBLE, , MPI_COMM_WORLD);
MPI_Bcast(output_freq_p, , MPI_INT, , MPI_COMM_WORLD);
MPI_Bcast(g_i_p, , MPI_CHAR, , MPI_COMM_WORLD);
# ifdef DEBUG
printf("Get_args, rank%2d n %d n_steps %d delta_t %e output_freq %d g_i %c\n",
my_rank, *n_p, *n_steps_p, *delta_t_p, *output_freq_p, *g_i_p);
fflush(stdout);
# endif
} void Build_cyclic_mpi_type(int loc_n)// 生成大小为 loc_n 的循环分配数据类型
{
MPI_Datatype temp_mpi_t;
MPI_Aint lb, extent;
MPI_Type_vector(loc_n, , comm_sz, vect_mpi_t, &temp_mpi_t);// 将跨度为 comm_sz 的 loc_n 个 “连续 2 个 double” 封装为 temp_mpi_t
MPI_Type_get_extent(vect_mpi_t, &lb, &extent);
MPI_Type_create_resized(temp_mpi_t, lb, extent, &cyclic_mpi_t);
MPI_Type_commit(&cyclic_mpi_t);
} void Gen_init_cond(double masses[], vect_t loc_pos[], vect_t loc_vel[], int n, int loc_n)
{
const double mass = 5.0e24, gap = 1.0e5, speed = 3.0e4;
if (my_rank == )
{
// srand(2);
for (int i = ; i < n; i++)
{
masses[i] = mass;
pos[i][X] = i * gap;
pos[i][Y] = 0.0;
vel[i][X] = 0.0;
// vel[i][Y] = speed * (2 * rand() / (double)RAND_MAX) - 1);
vel[i][Y] = (i % ) ? -speed : speed;
}
}
MPI_Bcast(masses, n, MPI_DOUBLE, , MPI_COMM_WORLD);
MPI_Scatter(pos, , cyclic_mpi_t, loc_pos, loc_n, vect_mpi_t, , MPI_COMM_WORLD);// loc_pos 和 loc_vel 需要分别分发
MPI_Scatter(vel, , cyclic_mpi_t, loc_vel, loc_n, vect_mpi_t, , MPI_COMM_WORLD);
# ifdef DEBUG
printf("Gen_init_cond, rank%2d %10.3e %10.3e %10.3e %10.3e %10.3e\n",
my_rank, masses[], loc_pos[][X], loc_pos[][Y], loc_vel[][X], loc_vel[][Y]);
fflush(stdout);
# endif
} void Get_init_cond(double masses[], vect_t loc_pos[], vect_t loc_vel[], int n, int loc_n)
{
if (my_rank == )
{
printf("For each particle, enter (in order): mass x-coord y-coord x-velocity y-velocity\n");
for (int i = ; i < n; i++)
{
scanf_s("%lf", &masses[i]);
scanf_s("%lf", &pos[i][X]);
scanf_s("%lf", &pos[i][Y]);
scanf_s("%lf", &vel[i][X]);
scanf_s("%lf", &vel[i][Y]);
}
}
MPI_Bcast(masses, n, MPI_DOUBLE, , MPI_COMM_WORLD);
MPI_Scatter(pos, , cyclic_mpi_t, loc_pos, loc_n, vect_mpi_t, , MPI_COMM_WORLD);// loc_pos 和 loc_vel 需要分别分发
MPI_Scatter(vel, , cyclic_mpi_t, loc_vel, loc_n, vect_mpi_t, , MPI_COMM_WORLD);
# ifdef DEBUG
printf("Get_init_cond, rank%2d %10.3e %10.3e %10.3e %10.3e %10.3e\n",
my_rank, masses[], loc_pos[][X], loc_pos[][Y], loc_vel[][X], loc_vel[][Y]);
fflush(stdout);
# endif
} void Output_state(double time, double masses[], vect_t loc_pos[], vect_t loc_vel[], int n, int loc_n)
{
MPI_Gather(loc_pos, loc_n, vect_mpi_t, pos, , cyclic_mpi_t, , MPI_COMM_WORLD);// loc_pos 和 loc_vel 需要分别聚集
MPI_Gather(loc_vel, loc_n, vect_mpi_t, vel, , cyclic_mpi_t, , MPI_COMM_WORLD);
if (my_rank == )
{
printf("Output_state, time = %.2f\n", time);
for (int i = ; i < n; i++)
printf(" %2d %10.3e %10.3e %10.3e %10.3e %10.3e\n", i, masses[i], pos[i][X], pos[i][Y], vel[i][X], vel[i][Y]);
printf("\n");
fflush(stdout);
}
} int First_index(int gbl1, int rk1, int rk2, int proc_count)
{
return gbl1 + (rk2 - rk1) + (rk1 < rk2 ? : proc_count);
} int Local_to_global(int loc_part, int proc_rk, int proc_count)// 进程局部编号转全局编号
{
return loc_part * proc_count + proc_rk;
} int Global_to_local(int gbl_part, int proc_rk, int proc_count)// 全局编号转进程局部编号
{
return (gbl_part - proc_rk) / proc_count;
} void Compute_force_pair(double m1, double m2, vect_t pos1, vect_t pos2, vect_t force1, vect_t force2)// 计算两个颗粒之间的引力
{
const double mg = -G * m1 * m2;
vect_t f_part_k;
double len, fact; f_part_k[X] = pos1[X] - pos2[X];
f_part_k[Y] = pos1[Y] - pos2[Y];
len = sqrt(f_part_k[X] * f_part_k[X] + f_part_k[Y] * f_part_k[Y]);
fact = mg / (len * len * len);
f_part_k[X] *= fact;
f_part_k[Y] *= fact;
force1[X] += f_part_k[X];
force1[Y] += f_part_k[Y];
force2[X] -= f_part_k[X];
force2[Y] -= f_part_k[Y];
} void Compute_proc_forces(double masses[], vect_t tmp_data[], vect_t loc_forces[], vect_t pos1[], int loc_n1, int rk1, int loc_n2, int rk2, int n, int p)
{ // 计算 pos1 与 tmp_data 中满足下标条件的颗粒之间的引力
//masses 颗粒质量表
//tmp_data 环形传输数据
//loc_forces 本进程颗粒受力数据
//pos1 本进程颗粒位置数据
//loc_n1 pos1 中颗粒数量
//rk1 pos1 中 process owning particles
//loc_n2 temp_data 中颗粒数量
int gbl_part1, gbl_part2, loc_part1, loc_part2; //rk2 tmp_data 中 process owning contributed particles
for (gbl_part1 = rk1, loc_part1 = ; loc_part1 < loc_n1; loc_part1++, gbl_part1 += p) //n 总颗粒数
{ //p 参与计算的进程数
for (gbl_part2 = First_index(gbl_part1, rk1, rk2, p), loc_part2 = Global_to_local(gbl_part2, rk2, p); loc_part2 < loc_n2; loc_part2++, gbl_part2 += p)
{
# ifdef DEBUG
printf("Compute_proc_forces before, rank%2d> part%2d %10.3e %10.3e part%2d %10.3e %10.3e\n",
my_rank, gbl_part1, loc_forces[loc_part1][X], loc_forces[loc_part1][Y], gbl_part2, tmp_data[loc_n2 + loc_part2][X], tmp_data[loc_n2 + loc_part2][Y]);
# endif
Compute_force_pair(masses[gbl_part1], masses[gbl_part2], pos1[loc_part1], tmp_data[loc_part2], loc_forces[loc_part1], tmp_data[loc_n2 + loc_part2]);
# ifdef DEBUG
printf("Compute_proc_forces before, rank%2d> part%2d %10.3e %10.3e part%2d %10.3e %10.3e\n",
my_rank, gbl_part1, loc_forces[loc_part1][X], loc_forces[loc_part1][Y], gbl_part2, tmp_data[loc_n2 + loc_part2][X], tmp_data[loc_n2 + loc_part2][Y]);
# endif
}
}
} void Compute_forces(double masses[], vect_t tmp_data[], vect_t loc_forces[], vect_t loc_pos[], int n, int loc_n)// 计算本线程各颗粒受到的引力
{ // masses 颗粒质量表
const int src = (my_rank + ) % comm_sz, dest = (my_rank - + comm_sz) % comm_sz; // tmp_data 环形传输数据
int i, other_proc, loc_part; // loc_forces 本进程颗粒受力数据
// loc_pos 本进程颗粒位置数据
memcpy(tmp_data, loc_pos, loc_n * sizeof(vect_t)); // 将本进程分到的位置数据放入 tmp_data // n 总颗粒数
memset(tmp_data + loc_n, , loc_n * sizeof(vect_t));// 初始化 tmp_data 的引力数据 // loc_n 本进程分到的颗粒数
memset(loc_forces, , loc_n * sizeof(vect_t)); // 初始化本进程引力数据 Compute_proc_forces(masses, tmp_data, loc_forces, loc_pos, loc_n, my_rank, loc_n, my_rank, n, comm_sz);// 计算本进程的颗粒间的引力作用
for (i = ; i < comm_sz; i++)// 计算本进程的颗粒与收到的新颗粒之间的引力作用
{
other_proc = (my_rank + i) % comm_sz;// 每次交换信息的对象不同
MPI_Sendrecv_replace(tmp_data, * loc_n, vect_mpi_t, dest, , src, , MPI_COMM_WORLD, MPI_STATUS_IGNORE);
Compute_proc_forces(masses, tmp_data, loc_forces, loc_pos, loc_n, my_rank, loc_n, other_proc, n, comm_sz);
}
MPI_Sendrecv_replace(tmp_data, * loc_n, vect_mpi_t, dest, , src, , MPI_COMM_WORLD, MPI_STATUS_IGNORE);
for (loc_part = ; loc_part < loc_n; loc_part++)
{
loc_forces[loc_part][X] += tmp_data[loc_n + loc_part][X];
loc_forces[loc_part][Y] += tmp_data[loc_n + loc_part][Y];
}
} void Update_part(int loc_part, double masses[], vect_t loc_forces[], vect_t loc_pos[], vect_t loc_vel[], int n, int loc_n, double delta_t)
{
const int part = my_rank * loc_n + loc_part;
const double fact = delta_t / masses[part];
# ifdef DEBUG// 输出在更新前和更新后的数据
printf("Update_part before, part%2d %10.3e %10.3e %10.3e %10.3e %10.3e %10.3e\n",
part, loc_pos[loc_part][X], loc_pos[loc_part][Y], loc_vel[loc_part][X], loc_vel[loc_part][Y], loc_forces[loc_part][X], loc_forces[loc_part][Y]);
fflush(stdout);
# endif
loc_pos[loc_part][X] += delta_t * loc_vel[loc_part][X];
loc_pos[loc_part][Y] += delta_t * loc_vel[loc_part][Y];
loc_vel[loc_part][X] += fact * loc_forces[loc_part][X];
loc_vel[loc_part][Y] += fact * loc_forces[loc_part][Y];
# ifdef DEBUG
printf("Update_part after, part%2d %10.3e %10.3e %10.3e %10.3e\n",
part, loc_pos[loc_part][X], loc_pos[loc_part][Y], loc_vel[loc_part][X], loc_vel[loc_part][Y]);
fflush(stdout);
# endif
} int main(int argc, char* argv[])
{
int n, loc_n, loc_part;
int n_steps, step;
double delta_t;
int output_freq;
double *masses;
vect_t* loc_pos; // *pos 变成了全局变量
vect_t* tmp_data; // 用于环形交换的数据
vect_t* loc_vel;
vect_t* loc_forces;
char g_i;
double start, finish; MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &comm_sz);
MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
MPI_Type_contiguous(DIM, MPI_DOUBLE, &vect_mpi_t);
MPI_Type_commit(&vect_mpi_t); Get_args(argc, argv, &n, &n_steps, &delta_t, &output_freq, &g_i);
loc_n = n / comm_sz;
Build_cyclic_mpi_type(loc_n);
masses = (double*)malloc(n * sizeof(double));
tmp_data = (vect_t*)malloc( * loc_n * sizeof(vect_t)); // 前半段 tmp_pos 后半段 temp_force
loc_pos = (vect_t*)malloc(loc_n * sizeof(vect_t));
loc_vel = (vect_t*)malloc(loc_n * sizeof(vect_t));
loc_forces = (vect_t*)malloc(loc_n * sizeof(vect_t));
if (my_rank == )
{
pos = (vect_t*)malloc(n * sizeof(vect_t));
vel = (vect_t*)malloc(n * sizeof(vect_t));
}
if (g_i == 'g')
Gen_init_cond(masses, loc_pos, loc_vel, n, loc_n);
else
Get_init_cond(masses, loc_pos, loc_vel, n, loc_n); if(my_rank == )
start = MPI_Wtime();
# ifdef OUTPUT
Output_state(0.0, masses, loc_pos, loc_vel, n, loc_n);
# endif
for (step = ; step <= n_steps; step++)
{
Compute_forces(masses, tmp_data, loc_forces, loc_pos, n, loc_n);
for (loc_part = ; loc_part < loc_n; Update_part(loc_part++, masses, loc_forces, loc_pos, loc_vel, n, loc_n, delta_t));
# ifdef OUTPUT
if (step % output_freq == )
Output_state(step * delta_t, masses, loc_pos, loc_vel, n, loc_n);
# endif
} if (my_rank == )
{
finish = MPI_Wtime();
printf("Elapsed time = %e ms\n", (finish - start) * );
free(pos);
free(vel);
}
MPI_Type_free(&vect_mpi_t);
MPI_Type_free(&cyclic_mpi_t);
free(masses);
free(tmp_data);
free(loc_forces);
free(loc_pos);
free(loc_vel);
MPI_Finalize();
return ;
}

● 输出结果,与基本算法类似。8 进程 16 体,3 秒,时间步长 1 秒,舍去 debug 输出 1.476754e+00 ms;8 进程 1024 体,3600 秒,时间步长 1 秒,舍去 output 和 debug 输出 1.329172e+04 ms,在较大数据规模上稍有优势

MPI n 体问题的更多相关文章

  1. MPI并行计算模拟N体问题

    实验内容 N体问题是指找出已知初始位置.速度和质量的多个物体在经典力学情况下的后续运动.在本次实验中,你需要模拟N个物体在二维空间中的运动情况.通过计算每两个物体之间的相互作用力,可以确定下一个时间周 ...

  2. Pthreads n 体问题

    ▶ <并行程序设计导论>第六章中讨论了 n 体问题,分别使用了 MPI,Pthreads,OpenMP 来进行实现,这里是 Pthreads 的代码,分为基本算法和简化算法(引力计算量为基 ...

  3. OpenMP n 体问题

    ▶ <并行程序设计导论>第六章中讨论了 n 体问题,分别使用了 MPI,Pthreads,OpenMP 来进行实现,这里是 OpenMP 的代码,分为基本算法和简化算法(引力计算量为基本算 ...

  4. 查找素数Eratosthenes筛法的mpi程序

    思路: 只保留奇数 (1)由输入的整数n确定存储奇数(不包括1)的数组大小: n=(n%2==0)?(n/2-1):((n-1)/2);//n为存储奇数的数组大小,不包括基数1 (2)由数组大小n.进 ...

  5. kmeans算法并行化的mpi程序

    用c语言写了kmeans算法的串行程序,再用mpi来写并行版的,貌似参照着串行版来写并行版,效果不是很赏心悦目~ 并行化思路: 使用主从模式.由一个节点充当主节点负责数据的划分与分配,其他节点完成本地 ...

  6. MPI Maelstrom - POJ1502最短路

    Time Limit: 1000MS Memory Limit: 10000K Description BIT has recently taken delivery of their new sup ...

  7. MPI之求和

    // MPI1.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include "mpi.h" #include &l ...

  8. VS2012下配置MPI

    并行处理结课实验,要用到MPI编程,我的电脑和VS2012都是64位的,以为MPICH也得是64位才行,结果饶了很大的弯——配置正确,添加引用之后,仍然无法识别MPI函数. 后来换了个32位的MPIC ...

  9. MPI+WIN10并行试运行

    系统:2015 win10专业版 x64 MPI安装包:mpich2-1.4.1p1-win-x86-64.man 将后缀改为.msi 以管理员身份安装 安装过程一路默认,注意<behappy为 ...

随机推荐

  1. Model/ModelMap 和 ModelAndView 的区别使用

    Model/ModelMap 和 ModelAndView 的区别使用 Model/ModelMap controller: package springmvc.controller; import ...

  2. UVALive-3645 Objective: Berlin (最大流:时序模型)

    题目大意:有n个城市,m条航班.已知每条航班的起点和终点,还有每条航班的载客量.出发时间.到达时间.并且要求在任何一个城市(起点.终点除外)都至少要有30分钟的中转时间,求起点到终点的最大客流量. 题 ...

  3. JAVA中的>>和>>>号以及<<号的作用

    public static void main(String[] args) { //右移2位,输出结果为2.二进制1000右移2位变为0010 System.out.println(8>> ...

  4. gradle ssh 插件

    org.hidetake.ssh Gradle SSH Plugin is a Gradle plugin which provides remote command execution and fi ...

  5. 2018.12.7 L190

    China called for the immediate release of Meng Wanzhou, chief financial officer of Huawei Technologi ...

  6. Sqlserver:datetime类型的精度(不确定性)问题

    转自http://www.xuebuyuan.com/212359.html 背景:近日进行大型数据表的迁移处理,遭遇创建 主键时 索引键值重复的错误.仔细检查原始表,并未有任何问题.分析后发现是迁移 ...

  7. VScode+Flutter 开发继续踩坑

    运行慢解决方法1:修改build.gradle,注释掉jcenter(),google().使用阿里的镜像.原因是jcenter google库无法访问到导致的问题.虽然我有万能的爬墙工具,开启全局代 ...

  8. 手动整合实现SSH项目开发02

    在bean包下建立User类和User.hbm.xml文件,实现User类和数据库表User的映射关系,具体User类不多说,User.hbm.xml如下: <?xml version=&quo ...

  9. java MessageFormat.format 用法

    FormatElement: { ArgumentIndex }:是从0开始的入参位置索引. { ArgumentIndex , FormatType } { ArgumentIndex , Form ...

  10. .net core microservices 架构之 分布式

    .net core microservices 架构之 分布式  一:简介   自动计算都是常驻内存的,没有人机交互.我们经常用到的就是console job和sql job了.sqljob有自己的宿 ...