背景
ある測定データ(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の処理をやっているのでメモリがカツカツになってしまう。
そこが改善されたからではないか?