MPI实现并行pagerank

MPI入门:实现并行pagerank算法

部分代码参考自:https://www.cnblogs.com/luyi07/p/11110658.html

首先对所需要的跳跃矩阵,PR矩阵进行初始化,代码如下,由0进程负责数据的加载和初始化,然后使用MPI_Scatter将原数据分块后分散到各个进程中去。分块操作由MPI_Scatter自行完成。MPI_Scatter函数接口如下:

int MPI_Scatter(
			void* 			send_buf_p,		/* IN */
			int 			send_count,		/* IN */
			MPI_Datatype 	send_type,		/* IN */
			void*			recv_buf_p,		/* OUT */
			int				recv_count,		/* IN */
			MPI_Datatype	recv_type,		/* IN */
			int				src_proc,		/* IN */
			MPI_Comm		comm			/* IN */
			)

其中send_count是为每个进程分配数据的大小,一般为data_count/comm_size,数据总数除以进程数。注意这里0进程使用MPI_Scatter进行分散的时候,0进程依然会得到“一块“数据的。其次,需要考虑分散后剩余数据的处理(可能存在均匀分配后剩余的数据)。参数src_proc确定了发送进程的编号。

#include<fstream>
#include<mpi.h>
#include<iostream>
using namespace std;
bool IniData(
        double m[],        //全局邻居矩阵
        double local_m[], //存放分片的邻居矩阵
        double pr[],       //进程计算的PR值
        double b[],       //原始的PR值
        int line,         //分片大小
        int size,         //网页数
        int my_rank,      //进程编号
        MPI_Comm comm     //通信子
            )
{
    if(my_rank==0)
    {
        //初始化邻接矩阵、PR矩阵和初始PR矩阵
        for(int i=0;i<size;i++){
            for(int j=0;j<size;j++){
                m[ i*size + j ] = 0;
            }
            b[i] = 1/(double)size;
            pr[i] = 1/(double)size;
        }
        
        //从文件中读取数据
        ifstream Input;
        Input.open("data.txt");
        int s,d;
        while(Input >>s >>d)
        {
            m[ d*size + s] =1;
        }
        //变为转移矩阵
        for(int i=0;i<size;i++)
	{
            int cout=0;
            for(int j=0;j<size;j++)
            {	
                if(m[j*size+i]==(double)1)
                    cout++;
            }
            if(cout>0)
            {
		for(int k=0;k<size;k++)
		{	
                	m[k*size+i]=m[k*size+i]/(double)cout;
		}
            }
		
	}
        //将分块的数据分发给各个进程,包括自己。
        MPI_Scatter(m, line * size, MPI_DOUBLE, local_m, line * size, MPI_DOUBLE, 0, comm );
        
    }
    else
    {
        //接受转移矩阵
        MPI_Scatter(m, line * size, MPI_DOUBLE, local_m, line * size, MPI_DOUBLE, 0, comm );
    }
    return true;
}

对于0进程调用MPI_Scatter为发送数据块给各个进程,对于其他进程,MPI_Scatter是接受0进程发送的数据块。另外MPI_Scatter是带有阻塞的,即接收方收到后才会进行后面的操作。

下面是并行计算部分的代码
int main(){

    double alpha=0.85;
    int my_rank;
    int num_procs;
    int size = 4;
    double start, finish;
    
    MPI_Init(NULL,NULL);
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
    MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
    
    int line = size / num_procs;
    cout<<" line = "<<line<<endl;
   
    double  m[size*size];           //整体的转移矩阵
    double  local_m[ line * size ]; //分发到各个进程中的分片矩阵信息
    double  b [size];               //初始概率矩阵,用于 计算陷阱问题
    double  pr[size];               //计算PR值
    double  local_pr[line];
    
    //初始化个数据并发送给各进程
    IniData(m,local_m,pr,b,line,size,my_rank,MPI_COMM_WORLD);
    //所以Barrier是在点对点通信中才需要,这里不需要
    //MPI_Barrier(MPI_COMM_WORLD);
    
    for(int it=0;it<10;it++)
    {
        if( my_rank == 0 ){

            //start = MPI_Wtime();
            //广播每轮的pr值

            MPI_Bcast(pr, size, MPI_DOUBLE, 0, MPI_COMM_WORLD);
            //进程0计算自己分到的块
            for(int i= 0; i< line;i++){
                double temp = 0;
                for(int j=0;j<size;j++){
                    temp += local_m[i*size+j] * pr[j];
                }
                local_pr[i] = temp;
                
            }

            //进程0负责计算分配之后剩下的
            for(int i= num_procs *line; i< size;i++){
                double temp = 0;
                for(int j=0;j<size;j++){
                    temp += m[i*size+j] * pr[j];
                }
                pr[i] = temp;
            }

            //获取其他进程矩阵相乘后的结果
            MPI_Gather( local_pr, line, MPI_DOUBLE, pr, line, MPI_DOUBLE, 0, MPI_COMM_WORLD );
          
            //for(int i=0;i<size;i++)
            //    cout<<pr[i]<<endl;
            //修正后的公式
            for(int i=0;i<size;i++)
            {
                pr[i]=alpha*pr[i]+(1-alpha)*b[i];
               // cout<<"pr"<<pr[i]<"\t";
            }
            
            //MPI_Bcast(pr,size,MPI_DOUBLE,0,MPI_COMM_WORLD);
            if(it==9)
            {
                FILE *fp = fopen("result.txt","w");
                        for(int i=0;i<size;i++){
                               // for(int j=0;j<size;j++)
                                        fprintf(fp,"%lf\t",pr[i]);
                                fputc('\n',fp);
                        }
                        fclose(fp);
            }
            //finish = MPI_Wtime();
            //printf(" time: %lf s \n", finish - start );
        }
        else{
            //double * buffera = new double [ line*size ];
            //double * bufferc =new double [size];
           //接受PR矩阵
            MPI_Bcast(pr, size, MPI_DOUBLE, 0, MPI_COMM_WORLD);
//            for(int i=0;i<size;i++)
//            {
//                cout<<"第"<<it<<"次 pr"<<pr[i]<<"\t";
//            }
            /*
            cout<<" b:"<<endl;
            for(int i=0;i<size;i++){
                for(int j=0;j<size;j++){
                    cout<<b[i*size + j]<<",";
                }
                cout<<endl;
            }
            */
            for(int i= 0; i< line;i++){
                double temp = 0;
                for(int j=0;j<size;j++){
                   // cout<<"local_m"<<local_m[i*size+j]<<"pr"<<pr[j]<<endl;
                    temp += local_m[i*size+j] * pr[j];
                }
                local_pr[i] = temp;
                //cout<<"local_pr"<<local_pr[i]<<"\t";
            }


            /*
            cout<<" ans:"<<endl;
            for(int i=0;i<line;i++){
                for(int j=0;j<size;j++){
                    cout<<ans[i*size + j]<<",";
                }
                cout<<endl;
            }
            */
            
            MPI_Gather(local_pr, line, MPI_DOUBLE, pr, line, MPI_DOUBLE, 0, MPI_COMM_WORLD );
            //delete [] buffera;
        }
        //MPI_Barrier(MPI_COMM_WORLD);
   }
   // delete [] m, b, local_m, pr;

    MPI_Finalize();
    return 0;
}

函数接口MPI_Scatter()是一对多的发送接口,而MPI_Gather()则是多对一的发送接口了,由各个进程计算完成后使用MPI_Gather()将计算结果发送给进程0做最后的汇总。MPI_Gather()的原型为:

int MPI_Gather(
			void* 			send_buf_p,		/* IN */
			int 			send_count,		/* IN */
			MPI_Datatype 	send_type,		/* IN */
			void*			recv_buf_p,		/* OUT */
			int				recv_count,		/* IN */
			MPI_Datatype	recv_type,		/* IN */
			int				dest_proc,		/* IN */
			MPI_Comm		comm			/* IN */
			)

MPI_Bcast()是用来进行组内广播的,在每次迭代开始之前,将上一轮计算结果广播给各个进程用来进行下一次计算。使用集合通信的方法来实现pagerank算法是同步并行计算,其次使用矩阵存储的话,比较占内存空间,所以需要做很多优化,之后会慢慢改进。

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值