0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Raspberry PI 4 で MPI(並列処理)をやってみた

Last updated at Posted at 2024-12-21

きっかけ

 前回、ネットワークブートでラズパイを起動してみた。ネットワークブートで複数のラズパイを同じ環境で起動できたので、MPI(並列処理)をやってみようと思う。

環境構築

  • Raspberry Pi4でネットワークブートするに記載した手順でネットワークブート環境を構築し、以下のようなクラスタを構築する。
    注意1:全てのノードに同じユーザアカウントを作成すること(異なるユーザアカウントだと実行できない)
    注意2:ブートサーバー側、クライアント側は相互にパスワード無しの鍵認証のsshでアクセスできる状態にすること(送受信メッセージのやり取り等にsshを使っている模様)
    注意3:各クライアント側は起動元の領域を共通にしているので、各クライアント個別で異なるファイルを保存することができない。また、swapも無効にしたほうが良いと思われる。(あるノードが他ノードのメモリマネジメントなんて把握しているとは思えないので。。。)
    node.png

  • OpenMPIのインストール
    • ブートサーバー側、クライアント側それぞれにMPIで必要なソフトをインストールする
      $ sudo apt install openmpi-bin openmpi-common openmpi-doc libopenmpi-dev libgtk2.0-dev librdmacm-dev -y
    
    • ブートサーバー側、クライアント側でOpenMPIのバージョンが一致していることを確認する
      注意:(おそらく)メジャーバージョンが一致していないと実行できない
      $ mpirun --version
      mpirun (Open MPI) 4.1.4
      
      Report bugs to http://www.open-mpi.org/community/help/
    

  • 設定ファイルの作成
     新規ファイルを作成し、各ノードのIPアドレスとrank数(コア数)を記述する
hosts
192.168.0.aaa cpu=4
192.168.0.bbb cpu=4
192.168.0.ccc cpu=4
192.168.0.ddd cpu=4

プログラム作成

  • 試しにプログラムを作成
    500000000以上の素数を計算し、表示するプログラムを前の手順で作成したhostsと同じディレクトリに作成する
prime-number_mpi.cpp
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<unistd.h>
#include <stdio.h>
#include <mpi.h>
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdint>

void searchPrimeNum(int rank, long& i,long& number);

int main()
{
  long i;
 
  long number = 500000000; //計算を始める数
  long thre_number;
  
  int nproc,rank;
  MPI_Init(NULL, NULL); //MPIを初期化
  MPI_Comm_size(MPI_COMM_WORLD, &nproc); //クラスタの総ランク数(コアの数)を取得
  MPI_Comm_rank(MPI_COMM_WORLD, &rank); //各ランクの番号を取得
  printf("%d in %d\n", rank, nproc); //各ランクの番号と総ランク数を表示
  MPI_Barrier(MPI_COMM_WORLD); //各ランクの処理の同期をここでとる(全てのランクがここまで処理が進むと、これ以降の処理に進むことができる)
 
while (number > 0) {

	thre_number = number + (long)rank ;
	if(rank != 0){
		searchPrimeNum(rank, i, thre_number); //rank0以外に素数の計算をさせる
	}
	MPI_Barrier(MPI_COMM_WORLD); //各ランクの処理の同期をとる
	if(rank == 0){
		for(int j = 1; j < nproc; ++j ){

			MPI_Recv(&thre_number, 1, MPI_LONG, j, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); //rank0 はそれ以外のrankから素数の計算結果を受信する
			if( thre_number != -1){
				std::cout << thre_number  << std::endl; //素数であるならば表示する(素数でないなら-1を代入しているので表示しない)
			}

		}
	}

	number = number + nproc -1;

}
MPI_Finalize();
return 0;

}

void searchPrimeNum(int rank, long& i, long& number){
	int flag = 0;
	for( i=2;i<number;++i ) {
		if( number%i==0 ) { 
			flag = 1;
			number = -1; //素数でない場合は-1を代入 (2以上で自分自身以外で割れる値がある場合)
			break;
		}
	}
	MPI_Send(&number , 1, MPI_LONG, 0, 0, MPI_COMM_WORLD); //rank0に値を送信する(素数であるならその値を、素数でないなら-1を送信)
}

  • コンパイルする
$ mpic++ -o prime-number_mpi prime-number_mpi.c

  • コンパイルした実行形式prime-number_mpiをクライアント側のホームディレクトリにコピーする(各ノードに同じ実行形式が無いと実行できない)

並列処理の実行

  • 以下コマンドで並列処理を実行する
$ mpirun -hostfile hosts -np 16 ./prime-number_mpi
#-hostfile でノードの情報を指定する
#-np で 総ランク数を指定する

  • 実行結果は以下の通り
0 in 16
1 in 16
2 in 16
3 in 16
4 in 16
12 in 16
5 in 16
13 in 16
6 in 16
14 in 16
7 in 16
15 in 16
11 in 16
8 in 16
9 in 16
10 in 16
500000003
500000009
500000041
500000057
500000069
500000071
500000077
500000089
500000093
500000099
500000101
.
.
.

ラズパイ1個のみで同様の処理を実行した場合と比べると、上記並列実行によりやや速くなったが、
並列実行させているrank数分(15倍)の速さにはなっていない模様。通信のレイテンシ等、遅くなるのかもしれない。

参考にしたサイト

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?