ノート
訪問者数 4231      最終更新 2009-02-15 (日) 17:43:43

Fedora10でMPICH2

09/02/06〜12 インストール周り

09/02/12 使い方とテスト

ためしてみよう。

プログラム1 hello.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "mpi.h"
#define MSG_LEN 100

int main(int argc, char* argv[]){
  int my_rank;
  int tag = 0;
  char message[MSG_LEN];

  /* 全てのMPI関数を呼び出す前に一回だけ呼び出す */
  MPI_Init(&argc, &argv);

  /* 自分のランクを調べる */
  MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

  /* ランク毎に処理を分岐 */
  /* ランク0以外のプロセスの処理 */
  if(my_rank != 0){
    int dest = 0; /* 送信先 */

    sprintf(message, "Hello, my process rank is %d", my_rank);

    /* ランク0のプロセスにメッセージ送信 */
    MPI_Send(message, strlen(message)+1, MPI_CHAR, dest, tag, MPI_COMM_WORLD);
  }
  /* ランク0のプロセスの処理 */
  else{
    int process_num; /* プロセス数 */
    int source;      /* 送信元 */
    MPI_Status recv_status; /* 受信時のステータス */

    /* プロセス数を調べる */
    MPI_Comm_size(MPI_COMM_WORLD, &process_num);

    for(source = 1; source < process_num; source++){
      /* ランク1〜process_numのプロセスからメッセージを受信 */
      MPI_Recv(message, MSG_LEN, MPI_CHAR, source, tag, MPI_COMM_WORLD,
               &recv_status);
      fprintf(stdout, "%s\n", message);
    }
  }

  /* MPI関数を呼び出した後,最後に一回だけ呼び出す */
  MPI_Finalize();
  exit(EXIT_SUCCESS);
}

では、これをコンパイルすると

mpicc -o hello hello.c

エラーメッセージは無く、ファイルhelloができる。
次に実行してみよう。まずmpd --ncpus=8 でmpdサーバー(デーモン)を起動する。

mpd --ncpus=8 &

何もメッセージなし。エラーなく起動したらしい。では並列実行開始。

$ mpiexec -n 8 hello
Hello, my process rank is 1
Hello, my process rank is 2
Hello, my process rank is 3
Hello, my process rank is 4
Hello, my process rank is 5
Hello, my process rank is 6
Hello, my process rank is 7

1行目: mpiexecで並列起動。-n 8で8並列を指定。ホストoreganoは8並列まで可能。
     実行するプログラムは hello 。このディレクトリには実行パスが通っているので、./はさぼった。 2行目以降: 実行結果。1つのCPUがマスターとなり、残り7つがこれらのメッセージを返す。

では数値積分の例

プログラムは

#include <stdio.h>
#include <math.h>
#include "mpi.h"

//#define DIV_NUM 160000  /* 全体の閾値の分割数  */
#define DIV_NUM 160000000  /* 全体の閾値の分割数  */

int main(int argc, char* argv[])
{
	int div_num = DIV_NUM;
	int my_rank;
	int tag = 0;
	int dest;			/* 送信先 */
	int process_num;		/* プロセス数 */
	int source;			/* 送信元 */
	
	int i;
	double time1,time2;		/* 実行時間測定用変数 */
	int start_i;			/* プロセッサ毎の閾値の最小値  */
   	int end_i;			/* プロセッサ毎の閾値の最大値  */
	int block_size;			/* プロセッサ毎のストリップ数  */
	double x;

	MPI_Status status;

	double h,pi,s,S=0.0;

	double my_x;
	int my_i, my_maxi=100000000;

/* 全てのMPI関数を呼び出す前に一回だけ呼び出す */
	MPI_Init(&argc, &argv);


	time1 = MPI_Wtime();

/* 自分のランクを調べる */
	MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

/* プロセス数を調べる */
	MPI_Comm_size(MPI_COMM_WORLD, &process_num);

/* ランク0以外の処理 */
	if(my_rank != 0)
	{
		dest = 0;
	/* プロセス毎の閾値の算出 */
		block_size = DIV_NUM/(process_num-1);
		start_i = block_size * (my_rank-1);
		end_i = start_i + block_size -1;
               if (my_rank == process_num-1) {
			end_i = DIV_NUM;
		}
		//printf("my_rank:%d start_i:%d end_i:%d\n", my_rank, start_i, end_i); 

	/* 積分の計算 */
		s = 0.0;
		h = 1.0/((double) DIV_NUM);
		for(i=start_i; i<=end_i; i++) {
			x = h * ((double) i);
			s = s + sqrt(1.0 - (x*x));
		}

		//printf("%f\n",s);
	/* ランク0にメッセージを送信 */		
		MPI_Send(&s, 1, MPI_DOUBLE, dest, tag, MPI_COMM_WORLD);
		
	}

/* ランク0の処理 */	
	else
	{
		//printf("DIV_NUM:%d process_num:%d block_size:%d\n",DIV_NUM,process_num, DIV_NUM/(process_num-1)); 
	/* 他のプロセスからメッセージを受け取る */
		for(source = 1; source < process_num; source++)
		{
			MPI_Recv(&s, 1, MPI_DOUBLE, source, tag, MPI_COMM_WORLD, &status);
			S += s;
		}
		h = 1.0/((double) DIV_NUM);
		pi = (S - 0.5) * h * 4.0;	/* 面積から円周率を計算 */

	/* 積分結果の印刷 */
		printf("N = %d, S = %.20f, pi = %.20f\n",DIV_NUM,S,pi);
	
	/* 時間表示 */
		time2 = MPI_Wtime();
		printf("time = %e seconds\n",time2 - time1);

	}
/* MPI関数を呼び出した後、最後に1回だけ呼び出す */
	MPI_Finalize();
}

原理は、1つのCPUをマスターに、残りを計算に割当てる。計算CPUは全体でDIV_NUM個のストリップ(台形)のうち、左から自分のプロセッサ番号番目の場所の面積計算を行う。マスターは最後に計算CPUから結果を受信して、足し合わせる。

分割のつくりはなるべく整数によった。右端は1/DIV_NUMが割り切れないことを考えて、端の値に強制した。幅hの掛け算はすべて総和を計算してから行った。また、台形法では両端は1/2回ずつでよいのだが、変な条件を付けずすべてのプロセッサで同じに計算しておいて、最後に両端の1/2分を引いた。具体的には左端の関数値1.0の1/2の0.5を引いた。右端は関数値が0なので引かなくてよい。

結果は次のようになった。

mpiexec -n 8 pi_MPI_090215
N = 160000000, S = 125663706.64357584714889526367, pi = 3.14159265358939610024
time = 1.039691e+00 seconds
mpiexec -n 2 pi_MPI_090215
N = 160000000, S = 125663706.64357669651508331299, pi = 3.14159265358941741653
time = 7.150658e+00 seconds

計算結果は、3.141592653589 で次の桁が3や4である。また、時間とCPU数の関係は、

CPU数  2(1)    3(2)    5(4)    8(7)
時間     7.15    3.58    1.79    1.04

但しCPU数は、カッコ無が全CPU数、カッコ内が計算CPU数である。


トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2009-02-15 (日) 17:43:43 (3026d)