RのRmpi,snowライブラリを使ってみる
- まず、lamboot。15マシンをlambootする
$ lamboot -v lamhostsLAM 7.1.2/MPI 2 C++/ROMIO - Indiana University
n-1<21042> ssi:boot:base:linear: booting n0 (localhost1)
n-1<21042> ssi:boot:base:linear: booting n1 (localhost2)
n-1<21042> ssi:boot:base:linear: booting n2 (localhost3)
n-1<21042> ssi:boot:base:linear: booting n3 (localhost4)
n-1<21042> ssi:boot:base:linear: booting n4 (localhost5)
n-1<21042> ssi:boot:base:linear: booting n5 (localhost6)
n-1<21042> ssi:boot:base:linear: booting n6 (localhost7)
n-1<21042> ssi:boot:base:linear: booting n7 (localhost8)
n-1<21042> ssi:boot:base:linear: booting n8 (localhost9)
n-1<21042> ssi:boot:base:linear: booting n9 (localhost10)
n-1<21042> ssi:boot:base:linear: booting n10 (localhost11)
n-1<21042> ssi:boot:base:linear: booting n11 (localhost12)
n-1<21042> ssi:boot:base:linear: booting n12 (localhost13)
n-1<21042> ssi:boot:base:linear: booting n13 (localhost14)
n-1<21042> ssi:boot:base:linear: booting n14 (localhost15)
n-1<21042> ssi:boot:base:linear: finished
- その上でRを起動し、Rmpiライブラリとsnowライブラリを読み込み、10 slavesを発生させる
> library(Rmpi)
> library(snow)
> mpi.spawn.Rslaves(nslaves=10)
10 slaves are spawned successfully. 0 failed.
master (rank 0 , comm 1) of size 11 is running on: localhost1
slave1 (rank 1 , comm 1) of size 11 is running on: localhost1
slave2 (rank 2 , comm 1) of size 11 is running on: localhost1
slave3 (rank 3 , comm 1) of size 11 is running on: localhost2
slave4 (rank 4 , comm 1) of size 11 is running on: localhost2
slave5 (rank 5 , comm 1) of size 11 is running on: localhost3
slave6 (rank 6 , comm 1) of size 11 is running on: localhost3
slave7 (rank 7 , comm 1) of size 11 is running on: localhost4
slave8 (rank 8 , comm 1) of size 11 is running on: localhost4
slave9 (rank 9 , comm 1) of size 11 is running on: localhost5
slave10 (rank 10, comm 1) of size 11 is running on: localhost5
>
- localhost1にmasterが発生し、2つのslaveが発生した。残りのノードには、localhost2に2つ、localhost3に2つ、localhost4に2つ、localhost5に2つ、と2つずつ、slaveが発生した。CPUが2つなので、2つずつのslaveが発生させられたのだと思う。このことは、15ノードマシン、の15x2=30CPUに対して、40のslavesを発生させたときの割り振り具合からも、それらしいことがわかる。以下は、別法として、makeCluster関数を用いて、"MPI"仕様で、まず5つのslavesを作っている。オブジェクトclはそれぞれのslaveをID付けして管理するオブジェクトである
- makeClusterでMPI仕様用のクラスターを5つ発生させてみる
> cl <- makeCluster(5, type = "MPI")
5 slaves are spawned successfully. 0 failed.> clusterCall(cl, function() Sys.info()[c("nodename","machine")])
[[1]]
nodename machine
"localhost1.localdomain" "i686"[[2]]
nodename machine
"localhost1.localdomain" "i686"[[3]]
nodename machine
"localhost2.localdomain" "i686"[[4]]
nodename machine
"localhost2.localdomain" "i686"[[5]]
nodename machine
"localhost3.localdomain" "i686"
- Rで作ったclusterオブジェクトは、『自分の使うべきマシン』をそれぞれlocalhostXとして割り当てられていることをnodenameとして知っていることがわかる。今、機器全体としては15台のPCがクラスタとしてまとまっており、その中で、Rのclusterオブジェクトが5つを使おうとしている。
- 今、一度、stopCluster(cl)をして、クラスタを解散した上で、今度は、40(CPU総数よりも多い)slavesを発生させてみる。少し長くなるが、localhost1からlocalhost15まで2つずつの割り当てがなされ、一巡すると、31番目のslaveはlocalhost1に戻り、また、1ノードに2 slavesが割り当てられている。
> stopCluster(cl)
[1] 1
> cl<-makeCluster(40,type="MPI")
40 slaves are spawned successfully. 0 failed.
> clusterCall(cl, function() Sys.info()[c("nodename","machine")])
[[1]]
nodename machine
"localhost1.localdomain" "i686"[[2]]
nodename machine
"localhost1.localdomain" "i686"[[3]]
nodename machine
"localhost2.localdomain" "i686"[[4]]
nodename machine
"localhost2.localdomain" "i686"[[5]]
nodename machine
"localhost3.localdomain" "i686"[[6]]
nodename machine
"localhost3.localdomain" "i686"[[7]]
nodename machine
"localhost4.localdomain" "i686"[[8]]
nodename machine
"localhost4.localdomain" "i686"[[9]]
nodename machine
"localhost5.localdomain" "i686"[[10]]
nodename machine
"localhost5.localdomain" "i686"[[11]]
nodename machine
"localhost6.localdomain" "i686"[[12]]
nodename machine
"localhost6.localdomain" "i686"[[13]]
nodename machine
"localhost7.localdomain" "i686"[[14]]
nodename machine
"localhost7.localdomain" "i686"[[15]]
nodename machine
"localhost8.localdomain" "i686"[[16]]
nodename machine
"localhost8.localdomain" "i686"[[17]]
nodename machine
"localhost9.localdomain" "i686"[[18]]
nodename machine
"localhost9.localdomain" "i686"[[19]]
nodename machine
"localhost10.localdomain" "i686"[[20]]
nodename machine
"localhost10.localdomain" "i686"[[21]]
nodename machine
"localhost11.localdomain" "i686"[[22]]
nodename machine
"localhost11.localdomain" "i686"[[23]]
nodename machine
"localhost12.localdomain" "i686"[[24]]
nodename machine
"localhost12.localdomain" "i686"[[25]]
nodename machine
"localhost13.localdomain" "i686"[[26]]
nodename machine
"localhost13.localdomain" "i686"[[27]]
nodename machine
"localhost14.localdomain" "i686"[[28]]
nodename machine
"localhost14.localdomain" "i686"[[29]]
nodename machine
"localhost15.localdomain" "i686"[[30]]
nodename machine
"localhost15.localdomain" "i686"[[31]]
nodename machine
"localhost1.localdomain" "i686"[[32]]
nodename machine
"localhost1.localdomain" "i686"[[33]]
nodename machine
"localhost2.localdomain" "i686"[[34]]
nodename machine
"localhost2.localdomain" "i686"[[35]]
nodename machine
"localhost3.localdomain" "i686"[[36]]
nodename machine
"localhost3.localdomain" "i686"[[37]]
nodename machine
"localhost4.localdomain" "i686"[[38]]
nodename machine
"localhost4.localdomain" "i686"[[39]]
nodename machine
"localhost5.localdomain" "i686"[[40]]
nodename machine
"localhost5.localdomain" "i686"
- 話しは少し戻して、5つのslavesをcl<-makeCluster(5,"MPI")で発生したところに戻る。この5つのslavesに計算をさせる。
- クラスターオブジェクトにひとつずつ計算を割り振る方法
- clusterApply(cl,1:2,f,3)
- 今、clは5つのslavesを持っている。このclのそれぞれのslaveに、関数fとその引数とを渡しているのが、このclusterApplyである。今、f<-function(a,b){a+b}なる、簡単な関数を考える。関数fは第1引数aと第2引数bとをとって、その和を返す関数である。clusterApply()関数は、第1引数にクラスターオブジェクト、第3引数に関数、第2引数にfに渡す第1引数、第4引数にfに渡す第2引数をそれぞれ指定している。これを今、クラスターが5つ、関数fの第1引数(clusterApplyの第2引数)が2つ、関数は1つ、関数fの第2引数(clusterApplyの第4引数)は1つであるので、第1と第2のslaveが使われて、第1のslaveでは、f(1,3)が、第2のslaveでは、f(2,3)が計算される。
- clusterApply(cl,1:2,f,3)
- クラスターオブジェクトにひとつずつ計算を割り振る方法
> clusterApply(cl,1:2,f,3)
[[1]]
[1] 4[[2]]
[1] 5
- 関数fが2変数を取る変わりに3変数をとるときには(たとえば、f<-function(a,b,c){a+b+c} )、clusterApply(cl,1:2,f,3,4) とすれば、第1slaveでは、1+3+4が、第2slaveでは、2+3+4が計算される。
- 今、clusterApplyの第2引数の要素数をclオブジェクトの要素の数より多くすると、以下のように計算できない。
> clusterApply(cl,1:6,f,3)
以下にエラーclusterApply(cl, 1:6, f, 3) : data lengh must be at most cluster size
- これでは不便なので、slaveが足りなければ、各slaveが複数の計算を担当してくれるのが望ましいので、そのときには、parLapply 関数を用いる。parLapply関数の引数の構成は、clusterApplyのそれと同じであるので、次のように、計算の数の問題が解消される。
> parLapply(cl,1:2,f,3)
[[1]]
[1] 4[[2]]
[1] 5> parLapply(cl,1:6,f,3)
[[1]]
[1] 4[[2]]
[1] 5[[3]]
[1] 6[[4]]
[1] 7[[5]]
[1] 8[[6]]
[1] 9> parLapply(cl,1:11,f,3)
[[1]]
[1] 4[[2]]
[1] 5[[3]]
[1] 6[[4]]
[1] 7[[5]]
[1] 8[[6]]
[1] 9[[7]]
[1] 10[[8]]
[1] 11[[9]]
[1] 12[[10]]
[1] 13[[11]]
[1] 14