RのRmpi,snowライブラリを使ってみる



  • まず、lamboot。15マシンをlambootする

$ lamboot -v lamhosts

LAM 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)
[[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