🐙

Python でMPI | モンテカルロ法で円周率を求めるプロセス数と処理時間

に公開

最近、コンピューターによる計算に興味が湧いてきたのでMPIについて少し実験してみました。

環境構築

WSLでUbuntuつかって、さらに仮想環境でやりました。
仮想の仮想です。

将来的には複数のラズパイを使ってクラスターでの計算も行ってみたいので、Linuxで動かすことにしました。

環境はこんな感じで作りました。

sudo apt update

sudo apt install openmpi-bin libopenmpi-dev

python3 -m venv ~/mpi-env

source ~/mpi-env/bin/activate

(mpi-env) user@hostname:~$           <---------こんな感じになればOK

あとはMPIをするためのライブラリをインストールすればOK。
これ以外の必要なライブラリは都度インストールしました。numpyとかあると便利だと思います。

pip install mpi4py

実験内容

実験内容はモンテカルロ法で円周率を求めるものです。
この処理を複数のプロセスで行い、どのくらい早くなるかを確かめます。

ちなみに、こういうプロセス数を増やしてどのくらい早くなるかを確認する方法をStrong Scalingといういらしいです。

実験方法

これを1つのプロセスでやるとき、2つ、4つ、6つ、8つで比較して、どのくらい早くなるかを確認しました。

ちなみに、モンテカルロ法とは面積から円周率を求める方法の一つです。

面積が1の四角形の中に4分の1の円(円弧)が描かれていて、その四角形にランダムに点をプロットするとき、その点はπ / 4の確率で円弧の中にプロットされることになる。
逆に、点が円弧の中にプロットされる確率の4倍は円周率に収束するという性質で円周率を求めるというものです。


Wikipediaより

コード

コードは以下のように書きました。解説もコメントしておきます。

monteCarlo.py
from mpi4py import MPI
import time
import random

# MPIをするためのランクとサイズの定義
# ランクとは → 自分は何個目のプロセスか
# サイズとは → 何個のプロセスで動かすか
comm = MPI.COMM_WORLD
rank = comm.Get_rank()
size = comm.Get_size()

# N回プロットします。今回は10の7乗
# 円弧の中にプロットされたものはCでカウント
N = 10 ** 7
C = 0

# 役割分担 仕事を各プロットに仕訳
chunk = N // size
start = rank * chunk
end = start + chunk

# 全プロセスのタイミングが揃うまで、他プロセスは待機させ、正確な時間を図る
comm.Barrier()
t_start = time.time()

# メイン処理です。各ランクは与えられた仕事量をこなす
for i in range(start, end):
    x = random.random()
    y = random.random()

    if x ** 2 + y ** 2 <= 1:
        C += 1

# 計算結果をランク0に集計
total_C = comm.reduce(C, op=MPI.SUM, root=0)

comm.Barrier()

# 実験が終わった最後の時間を取得
t_end = time.time()
local_time = t_end - t_start
max_time = comm.reduce(local_time, op=MPI.MAX, root=0)

# 結果の出力
if rank == 0:
    pi = 4 * total_C / N
    print("pi:", pi)
    print("elapsed:", max_time)

プログラムは以下のようにして実行します。
この例では4つのプロセスでプログラムが実行されます。

mpirun -np 4 python monteCarlo.py

実験結果

実験結果は以下のようになりました。
プロセスの数とその時にかかった実行時間です。

1つ:2.3249316215515137
2つ:1.219184160232544
4つ:0.7992208003997803
6つ:0.6270055770874023
8つ:0.6456313133239746

個人的に興味深いところは3つありました。

  1. プロセスを2つにすると計算時間はちゃんと約半分になる
  2. プロセスが6つよりも8つでプログラムを動かしたときの方がわずかですが多くの時間がかかっている
  3. 4つがコスパ良さそう

プロセス数が増えすぎると、BarrierやReduceでの待機時間が長くなったり、切り替えコストが高くなります。
なので必ずしもプロセスが増えるほど早くなるというわけではないということでした。

ちなみに求まった円周率はこんな感じでした。

pi: 3.141928

Discussion