▶ 《并行程序设计导论》第六章中讨论了 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. 通过电信ADSL无线猫WLAN上网的方法

    本教程只适合中国电信ADSL无线猫使用wifi(路由器不适合此帖)我的无线猫是电信赠送的华为[EchoLife]HG522c,亲测可用,解决网关无回应! 首先打开IE(注意,只能是IE,其他内核的浏览 ...

  2. vue全家桶实现笔记本功能

    一个通过vue实现的练手小项目,数据保存和导出通过node进行处理 成品截图: 安装vue-cli,webpack: cnpm install webpack -g cnpm install vue- ...

  3. UVALive-3268 Jamie's Contact Groups (最大流,网络流建模)

    题目大意:你的手机通讯录里有n个联系人,m个分组,其中,有的联系人在多个分组里.你的任务是在一些分组里删除一些联系人,使得每个联系人只在一个分组里并且使人数最多的那个分组人数最少.找出人数最多的那个分 ...

  4. hell 1>&2 2>&1 &>filename重定向的含义和区别

    当初在shell中, 看到">&1"和">&2"始终不明白什么意思.经过在网上的搜索得以解惑.其实这是两种输出. 一.linux重定 ...

  5. 关闭定时器(setTimeout/clearTimeout|setInterval/clearInterval)

    1.1 开启Timeout程序: scope.setTimeout("functionName()" | functionHandle, timeValue) 返回值:timerI ...

  6. BLE Android开发中的问题

    在此直说两个问题,第一是Android6.0 SDK23版本情况下开发的Android BLE APP,千万要记得在代码中申请到地理位置读取权限,否则你的APP在运行的时候会出现各种问题,另外就是除了 ...

  7. 我的octopress配置

    在github上用octopress搭建了自己的blog,octopress号称是"专门给黑客打造的博客(A blogging framework for 把hackers)",使 ...

  8. Yii Model

    REFs 自动生成代码 创建第一个Yii应用 创建模型 Yii 用户登陆机制

  9. python3 scrapy 使用selenium 模拟浏览器操作

    零. 在用scrapy爬取数据中,有写是通过js返回的数据,如果我们每个都要获取,那就会相当麻烦,而且查看源码也看不到数据的,所以能不能像浏览器一样去操作他呢? 所以有了-> Selenium ...

  10. Nodejs中npm install 命令的问题

    在使用nodejs的npm包管理工具中碰到过许多个坑,在网上查了很久才解决,现在加以总结. 两种安装方式(本地安装,全局安装) 1.全局安装(npm install -g moduleName/npm ...