Parallel Pythonを使ってRaspberry Pi 2 の4コアCPUを使いきってみる

# 動機
新発売のRaspberry Pi 2 はなんと4コアCPU。折角だから分散処理をさせて4コアをフルに使いきってみたい。

# やったこと
分散処理フレームワーク[Parallel Python](http://www.parallelpython.com/)を用いた簡単な分散処理プログラムを走らせ、旧モデルと新モデルの処理速度を比較してみた。
また、mpstatで各コアの使用率を確認した。

# 結論
旧モデルと比較して、4コア使い切ると**約8.5倍**、1コアだけでも**約2.4倍**高速、という結果になった。
４コア使いきった時、mpstatの結果も全コア100%となった。

# 所感
4コア使い切れたので満足である。
また、1コアだけでも十分速い事がわかった。[デスクトップ画面やChromeブラウザの起動が2.2〜2.6倍程度早くなったという報告があるが、](http://itpro.nikkeibp.co.jp/atcl/column/14/565123/020600005/?ST=oss&P=2)速度向上率が近いことから、１コア分の性能向上が大きく寄与しているのかもしれない。

# 実験方法と結果
## 分散処理の実行と処理時間測定
#### 与えた処理
1からNまでの自然数のうち偶数の個数を数える。数え方は、一つ一つ2で割った余りを求めて判定していく。
1からNまでの自然数をk分割し、k個のコアにそれぞれを分担させることで分散処理する。
N/2をすれば一発で求まる問題だが、敢えてつまらない仕事をさせてしまうことを先にラズパイに謝っておく。

今回はN=1000万 とした。

#### ソースコード
```python:pptest.py
# coding: UTF-8

import pp

# nがmの倍数かどうかを判断する
def ismultiples(n,m):
    return n % m == 0

# n1からn2まで(n2を含む)の自然数のうちmの倍数が何個あるか求める
def sum_multiples(n1,n2,m):
    cnt = 0
    for x in range(n1,n2+1):
        if ismultiples(x,m):
            cnt += 1
    return cnt

# 処理させるラズパイのIPアドレス
ppservers = ("192.168.1.241","192.168.1.241","192.168.1.241","192.168.1.241",) #4コア使用
# ppservers = ("192.168.1.241",) #1コア使用

# 自然数の最大値
N = 10000000
# 数えたい倍数
M = 2
# ノード数
num_node = len(ppservers)

# 接続先ノードを登録してサーバオブジェクトを生成
job_server = pp.Server(0, ppservers)
# タスクの生成
jobs = []
for i in range(num_node):
    # i番目のノードが扱う自然数の範囲を求める
    indSt = N/num_node*i+1
    if (i==num_node-1):
        indEnd = N
    else:
        indEnd = indSt+N/num_node-1
    # ノードにタスクを投げる
    jobs.append(job_server.submit(sum_multiples, (indSt, indEnd, M), (ismultiples,), ("math",)))
    print("task%d args:(%d,%d,%d)" % (i,indSt,indEnd,M))  

# 実行結果を回収. sum_mutiples()の戻り値を取得するイメージ.
# ノードでの処理がまだ終わっていない時は、ここの処理は終わるまでブロックされる.
result = 0;
for i in range(num_node):
    result += jobs[i]()

# 結果を表示
print "%d以下の自然数のうち %dの倍数の数 = %d" % (N, M, result)
job_server.print_stats()

```

####ソースコード上のポイント
ppserversに同じIPアドレスを4つ記述している。こうすることで同一ラズパイに４分割したタスクを投げる事になり、４コアをフルに使う事ができる。１コアしか使わないときは、1つだけ記述する。
もし、複数台のラズパイで分散処理させるときは、ここに各ラズパイのIPアドレスをセットすればよい。


#### Parallel Python のインストール
1. [Parallel Pythonのサイト](http://www.parallelpython.com/) からpp-1.6.4.tar.gzをダウンロードして解凍する
2. 解凍した先のフォルダに移動
3. インストール  
	`> python setup.py` 

#### スクリプトの実行
1. 処理を受け付けるサーバを起動する  
	`> ppserver.py &`
2. 記述したスクリプトpptest.pyを実行する  
	`> python pptest.py`

#### 実行結果

| 実行環境 | 処理時間 [sec] | 速度向上率 ※1 |
|:-----------|------------:|------------:|
| 旧モデル (Raspberry Pi B+)       |        48.7 |     -     |
| 新モデル (Raspberry Pi 2 B)　１コア使用     |      20.1 |    2.4    |
| 新モデル (Raspberry Pi 2 B)　４コア使用     |      5.7 |    8.5    |
※1  速度向上率=旧モデル処理時間/新モデル処理時間



スクリプトの出力は以下のとおり
##### 旧モデル (Raspberry Pi B+)
>task0 args:(1,10000000,2)
10000000以下の自然数のうち 2の倍数の数 = 5000000
Job execution statistics:
job count | % of all jobs | job time sum | time per job | job server
         1 |        100.00 |      48.4202 |    48.420249 | 192.168.1.241:60000
Time elapsed since server creation 48.7006518841
0 active tasks, 0 cores


##### 新モデル (Raspberry Pi 2 B)　１コアのみ使用
>task0 args:(1,10000000,2)
10000000以下の自然数のうち 2の倍数の数 = 5000000
Job execution statistics:
job count | % of all jobs | job time sum | time per job | job server
         1 |        100.00 |      19.6110 |    19.610974 | 192.168.1.241:60000
Time elapsed since server creation 20.0955970287
0 active tasks, 0 cores

##### 新モデル (Raspberry Pi 2 B)　４コア使用
>task0 args:(1,2500000,2)
task1 args:(2500001,5000000,2)
task2 args:(5000001,7500000,2)
task3 args:(7500001,10000000,2)
10000000以下の自然数のうち 2の倍数の数 = 5000000
Job execution statistics:
 job count | % of all jobs | job time sum | time per job | job server
         4 |        100.00 |      20.6152 |     5.153800 | 192.168.1.241:60000
Time elapsed since server creation 5.68198180199
0 active tasks, 0 cores

## mpstatで各コアのCPU使用率を見てみる
#### インストール
`sudo apt-get install sysstat`

#### 実行
1秒間隔で10回測定するときは次のようにする
`mpstat -P ALL 1 10`

別ターミナルでmpstatを動かしながら、上記のスクリプトを実行してみた。

#### 実行結果
##### 旧モデル (Raspberry Pi B+)
1コアしかないので、CPU番号は0しかない。%usrが100になっている。
>22:30:56     CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest   %idle
22:30:57     all  100.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00
22:30:57       0  100.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00

##### 新モデル (Raspberry Pi 2 B)　１コアのみ使用
4コアあるのでCPU番号が0〜3まである。CPU3だけが100%、それ以外は0%になっている。allは1/4で25.0%となっている。
>22:35:54     CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest   %idle
22:35:55     all   25.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00   75.00
22:35:55       0    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00
22:35:55       1    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00
22:35:55       2    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00
22:35:55       3  100.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00


##### 新モデル (Raspberry Pi 2 B)　４コア使用
4コア全部が100%になっている。満足。
>22:22:11     CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest   %idle
22:22:12     all  100.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00
22:22:12       0  100.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00
22:22:12       1  100.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00
22:22:12       2  100.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00
22:22:12       3  100.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00