背景
ある測定データ(csv)のグラフをPDFで保存したかった。
データ数が多く(1000データ)なると時間がたくさん(8minほど)かかってしまったので改善しようと思った。
目的
大量のPDFの保存を高速かつ、動作時にPCが固まらないようにしたい。
手法
pythonを用いて主に以下のことをした。
- 全測定データ(csv)を参照
 - 全測定データを配列に保存
 - 全測定条件データを読み込み
 - ひとデータごとに全データでループ ← これを高速化
- 測定データ・測定条件を受け取り(引数が複数)
 - matplotで描写
 - PDFで保存
 
 
そのまま
pythonで普通にfor loop
Treading
諸事情で実装経験があったのでやってみた
→こいつは1コアしか使えないので速度は大きく変わらない模様。
それでも4minも変わるのは意外
multiprocessing.Process
並列処理したい関数に引数が複数あるので、Processを利用した。
しかしいくらggってもスレッド数の制限方法が分からず、それは実装できなかった。
そのせいで、実行すると1000個の並列処理をするのでPCがカクカクになってしまった。
multiprocessing.Pool
引数は1つしか持てないが、スレッド数を簡単に指定できる。
以下のような関数をカマス事で複数引数にも対応できた。
def wrapper(self, args):
	return self.f(*args)
コーディング
multiprocessingのコーディングは以下のようにやればOK。
コーディングの仕方だけがわかる最小構成(並列化の意味はない)ものです。
詳細はこの記事などが参考になります。
Pythonの並列処理・並行処理をしっかり調べてみた - Qiita
from multiprocessing import Process, Pool
import multiprocessing
def f(a, b): # 並列処理する関数
    print(a, end=",") # 動作確認だけなのでシンプルに
def wrapper(args):  # Poolは引数を1つしか取れないので、これをかまして展開する。
    f(*args)
def main():
    # Processによる並列処理(複数引数:o, プロセス数指定:x)
    print('Process')
    p_list = []
    for i in range(20):
        p = Process(
            target=f,
            kwargs={'a': i, 'b': 0})
        p.start()  # 開始
        p_list.append(p)
    for p in p_list:
        p.join()  # 終了を待つ
    # Poolによる並列処理(複数引数:x, プロセス数指定:o)
    print('\nPool')
    p = Pool(multiprocessing.cpu_count())  # CPUの数の分並列処理
    values = []
    for i in range(20):
        values.append((i, 0))
    p.map(wrapper, values)  # 単数引数を複数に展開する
if __name__ == "__main__":
    main()
結果
Process
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
Pool
6,0,8,14,2,10,16,3,11,17,5,13,18,1,9,15,4,12,19,7,
結論
動作時間
以下のような順位になりました。
- multiprocessing(Poolによりスレッド数をPCのコア数に制限)
1min 50sec - multiprocessing(スレッド数の制限なし)
4min 30sec程度 - threading
5min 41sec - 並列化なし(pythonによる自動並列?のみ)
9min 33sec 
※試行錯誤の過程のメモなので、同等の条件で測定していない場合があります。目安程度に。
考察
1.と2.の比較をします。
1.は8個ずつ順に処理を進める事になります。
2.は全処理(今回は1000の処理)を同時並行で行う事になります。
- 
2.の方が早くなるはずではないのか?
実は、CPUは原理的に並列処理が不可能。
並列処理思われているものは、実は代わる代わるやることを変えているだけ。
例えば処理1、2、3の並列処理は
処理1(1μs実行)→処理2(1μs実行)→処理3(1μs実行)→処理1(さっきの続きから1μs実行)→・・・
というように並列に見せかけているだけである。
よって
処理1(終わるまで実行)→処理2(終わるまで実行)→処理3(終わるまで実行)
としても原理的には時間は変わらない。(こともある)
ただ、コア数(CPUの数)の分だけは並列処理が出来るので、それまでは処理が早くなる。 - 
なぜ早くなったのか?
メモリが豊富に使えるようになったから?
2.では1000の処理をやっているのでメモリがカツカツになってしまう。
そこが改善されたからではないか?