LoginSignup
14
11

More than 5 years have passed since last update.

Xubuntu16.04でOpenMPIを使って並列処理をしてみる

Last updated at Posted at 2016-06-05

OpenMPIの導入

aptを使ってOpenMPIのライブラリと実行環境?を導入する。ぐぐるとすぐ出てくるけど、パッケージ名が変わった(openmpi-devがlibopenmpi-devになった?)みたいなんで、次のコマンドで導入する。

$sudo apt-get install openmpi-doc openmpi-bin libopenmpi-dev

とりあえず並列処理でHello, World

とりあえず並列処理でHello, Worldをしてみる。プロセス番号とHello, Worldを表示する。言語はC言語で書きます。Fortranの人もそんなに変わらないかと…

Hello.c
#include <mpi.h>
#include <stdio.h>

int main( int args, char **argv )
{
   int rank, size;
   MPI_Init( &args, &argv ); // 並列処理の開始
   MPI_Comm_rank( MPI_COMM_WORLD, &rank ); // ランク(プロセス番号のようなもの)の取得
   MPI_Comm_size( MPI_COMM_WORLD, &size ); // 全ランク数の取得
   fprintf( stdout, "Hello, World from proc: [%d].\n", rank );
   MPI_Finalize(); // 並列処理の終わり
   return 0;
}

これをコンパイルするにはmpiccを使う。コンパイルオプションとかはgccと同じ感じなのかな?
とりあえず-Wall-oは使えました。

$mpicc Hello.c -o hello -Wall

コンパイルできたら実行する。実行するにはmpirunを使う。

$mpirun -np 4 ./hello
Hello, world from proc: 1.
Hello, world from proc: 0.
Hello, world from proc: 2.
Hello, world from proc: 3.

-npは並列処理するプロセスの数を指定するもの。今回は4を指定したので4つのプロセスから、それぞれfprintfを実行するので4つ表示される。綺麗に0から3まで並ばないのは、並列処理してるからで、それぞれにstdoutに書き込むから。Linuxの排他制御はよくしらないけど、たぶんMutex的なものな気がするから、先に取得した順に表示してる気がする。表示される順番は実行するたびに変わります。
ふつうに./helloで実行すると、並列処理がされないよ。

プロセス間通信の練習でn!を求めるプログラムを書いてみる

OpenMPIでは並列化するときにそれぞれのプロセスにランクと呼ばれる番号がつけられる。このランクを使ってプロセス間の通信ができる。今回はランク0のプロセスをまとめ役プロセスにして、ランク1~nのプロセスはランク0に自身のランクを送信して、ランク0のプロセスは送られてきたデータを加算していくことでn!を求めたいと思います。
というわけで、プログラムです。

factorial.c
#include <mpi.h>
#include <stdio.h>

int main( int argc, char **argv )
{
    int my_rank;
    int size;
    int local_result;
    int result = 0;
    MPI_Status status;

    int count;

    const int TAG = 0;
    const int LOAD = 0;
    const int DESTINATION = 0;

    MPI_Init( &argc, &argv );
    MPI_Comm_rank( MPI_COMM_WORLD, &my_rank );
    MPI_Comm_size( MPI_COMM_WORLD, &size );

    if( my_rank == 0 ) // まとめ役プロセスでの処理。 1~nプロセスからのデータを受け取って加算。
        for( count = 0; count < size; count++ ) {
            MPI_Recv( &local_result, LOAD, MPI_INT, cnt, TAG, MPI_COMM_WORLD, &status );
            fprintf( stdout, "Proc[%d]\n", local_result );
            result += local_result;
        }
        fprintf( stdout, "Total: %d.\n", result ;
    }
    else { // 1~nプロセスでの処理。 ランク0プロセスに向かって自身のランクを送信。
        MPI_Send( &my_rank, LOAD, MPI_INT, DESTINATION, TAG, MPI_COMM_WORLD );
    }

    MPI_Finalise();

    return 0;
}

実行するときのプロセス数-1がnなります。こんな感じに

$mpirun -np 11 factrial

ってすると、

Total: 55.

と、表示されます。
Linuxでforkとかしたことある人はなにやってるかわかると思いますが、簡単に動作を説明しておきます。
MPI_Initを実行すると、その時点から実行時に指定した数のプロセスが生成されます。ここで注意が必要なのは、MPI_Init以降のプログラムは生成されたプロセスそれぞれで実行されることです。
したがって、MPI_Comm_rankを実行することで、自プロセスのランクを取得して、そのランクをifで比較することでプロセスごとに処理を分岐します。

  • ランク0であれば、他のプロセスからのデータを待つ。
  • ランク0以外であれば、ランク0プロセスにデータを送信。

今回プロセス間通信にはMPI_SendMPI_Recvを使用しました。これはブロッキング型の通信関数です。ブロッキングとノンブロッキングはその手のプロの方がいらっしゃると思うのでここでは深い説明は避けますが、ブロッキングでは通信の処理が終了するまで全体の処理がそこで停止します。ノンブロッキングでは通信処理が完了しなくても次の処理に進んでしまいます。

MPI_SendMPI_Recvの引数は、

  • MPI_Send(void *buf, int count, MPI_Datatype type, int dest, int tag, MPI_Comm comm)
    • データ, データの個数, データ型, 宛先のランク番号, 荷物のタグ, コミュニケータ(まぁMPI_COMM_WORLDでいいんじゃないでしょうか…)
  • MPI_Recv(void *buf, int count, MPI_Datatype type, int source, int tag, MPI_Comm comm, MPI_Status *status)
    • データ, データの個数, データ型, 送信元のランク番号, 荷物のタグ, コミュニケータ(まぁMPI_COMM_WORLDでいいんじゃないでしょうか…), ステータス(Fortranのみ)

となっています。

14
11
1

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
14
11