前言
今天用fortran写了一个累加求和的程序,即:输入一个值,可以并行对程序累加求和,我相信这个程序可以帮新手了解fortran并行程序的结构。
然后想结合C语言的对应相同结构的代码探讨一下C和fortran的共同点。
一、C语言并行程序
代码如下:
#include <mpi.h>
#include <stdio.h>
#include <math.h>
double f(double a)
{
return 3*a*a+2*a;
}
int main(int argc, char * argv[])
{
int done=0,n,myid,numprocs,i;
double mypi,pi,sum;
double startwtime,endwtime;
int namelen;
char processor_name[MPI_MAX_PROCESSOR_NAME];
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
MPI_Comm_rank(MPI_COMM_WORLD,&myid);
MPI_Get_processor_name(processor_name,&namelen);
fprintf(stderr,"Process %d on %s\n",myid,processor_name);
fflush(stderr);
n=0;
if(myid==0)
{
printf("输入一个数字不超过:(0退出)");
fflush(stdout);
scanf("%d",&n);
startwtime=MPI_Wtime();
}
MPI_Bcast(&n,1,MPI_INT,0,MPI_COMM_WORLD);
if(n==0)
done=1;
else
{
sum=0.0;
for(i=myid+1;i<=n;i+=numprocs)
sum+=f(i);
mypi=sum;
MPI_Reduce(&mypi,&pi,1,MPI_DOUBLE,MPI_SUM,0,MPI_COMM_WORLD);
if(myid==0)
{
printf("结果%.16f\n",pi);
endwtime=MPI_Wtime();
printf("时间=%f\n",endwtime-startwtime);
}
}
MPI_Finalize();
return 0;
}
C语言在调用MPI包内的函数时比较直接,直接写函数名然后传参就可以了。
本程序主要用到MPI的函数是MPI_Bcast 和MPI_Reduce
其中MPI_Bcast 的作用是将变量n广播给所有线程,其函数范式如下
int MPI_Bcast(
void * data_p; //通信缓冲区的起始地址(可变)
int count; //通信缓冲区的数据个数
MPI_Datatype datatype; //通信消息缓冲区中的数据类型
int source_proc; //发送广播的根的序列号
MPI_Comm comm; //通信子
);
MPI_Reduce的函数范式为
int MPI_Reduce(
void *input_data, //指向发送消息的内存块的指针
void *output_data, //指向接收(输出)消息的内存块的指针
int count, //数据量
MPI_Datatype datatype,//数据类型
MPI_Op operator, //规约操作
int dest, //要接收(输出)消息的进程的进程号
MPI_Comm comm //通信器,指定通信范围
);
MPI_Reduce的主要用处就是把发送消息内存块的指针,发送到接受消息内存块的指针,以此用来实现进程的通信
在本代码中,先用以下命令广播变量n
MPI_Bcast(&n,1,MPI_INT,0,MPI_COMM_WORLD);
然后对数据进行计算,求出结果sum,并用Reduce将结果传递给pi,可以看到只有my_id为0时才可以执行输入输出操作,也就是进程号为0的进程负责广播变量和输出结果。
二、Fortran并行程序
代码如下:
program MPI_parallel
use mpi
implicit none
integer, parameter :: rk = 8
integer :: i, n_limit, ierr, numprocs, myid, c1, c2, c_rate
real(kind=rk) :: pi, picalc
call MPI_INIT(ierr)
call MPI_COMM_RANK(MPI_COMM_WORLD, myid, ierr)
call MPI_COMM_SIZE(MPI_COMM_WORLD, numprocs, ierr)
if(myid == 0) then
print *, "n_limit="
read(*,*) n_limit
end if
call MPI_BCAST(n_limit, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr)
pi = 0.0
call system_clock(c1, c_rate)
do i = myid + 1, n_limit, numprocs
pi = pi + 3*i*i+2*i
end do
call MPI_REDUCE( pi, picalc, 1, MPI_DOUBLE_PRECISION, MPI_SUM, 0, MPI_COMM_WORLD, ierr )
picalc = picalc
call system_clock(c2, c_rate)
if(myid == 0) then
write(*,*) picalc, real(c2-c1)/real(c_rate), numprocs
end if
call MPI_FINALIZE(ierr)
end program MPI_parallel
首先,fortran的导包是use mpi,然后同样的,为了输出结果,需要指定my_id为0的线程作为广播进程和将结果规约的进程。
但是在调用MPI函数时,前面需要加call,这是因为fortran在调用外部库的时候,需要用call来声明。
总结
总体而言,两程序的结构没有太大区别。