はじめに
Pythonで、機械学習やディープランニングをやっていると処理が重たくなって、性能面で問題になることがありますよね。
最近、私も重い処理があり性能改善のために並列処理を入れることにしました。
ローカルでjoblib
という並列処理用のライブラリを使い性能面で改善が見られれました。
しかし、開発環境上で同じ処理を実行したエラーに。。。
どうやら、uWSGI環境だとjoblibは動かないようです。
そのあたりの対処についてまとめました。
uWSGIとは
- uWSGIとはPythonでWebサービスを動かすためのアプリケーションサーバです。
環境情報
- python: 3.6.9
- nginx: 1.13.1
- uWSGI: 2.0.18
- joblib: 0.14.0
uWSGI上で失敗するjoblibのコード
- 並列化したい処理
length = 1000
def sum(i, j):
return i + j
- 並列化
from joblib import Parallel, delayed
sum_list = Parallel(n_jobs=-1)( [delayed(calc_sum)(i, j) for j in range(length) for i in range(length)])
- しかし、uWSGI上で動かすとこんな感じのエラーに
exception calling callback for <Future at 0x7fbc520c7eb8 state=finished raised TerminatedWorkerError>
Traceback (most recent call last):
File "/usr/local/lib/python3.7/site-packages/joblib/externals/loky/_base.py", line 625, in _invoke_callbacks
callback(self)
File "/usr/local/lib/python3.7/site-packages/joblib/parallel.py", line 309, in __call__
self.parallel.dispatch_next()
File "/usr/local/lib/python3.7/site-packages/joblib/parallel.py", line 731, in dispatch_next
if not self.dispatch_one_batch(self._original_iterator):
File "/usr/local/lib/python3.7/site-packages/joblib/parallel.py", line 759, in dispatch_one_batch
self._dispatch(tasks)
File "/usr/local/lib/python3.7/site-packages/joblib/parallel.py", line 716, in _dispatch
job = self._backend.apply_async(batch, callback=cb)
File "/usr/local/lib/python3.7/site-packages/joblib/_parallel_backends.py", line 510, in apply_async
future = self._workers.submit(SafeFunction(func))
File "/usr/local/lib/python3.7/site-packages/joblib/externals/loky/reusable_executor.py", line 151, in submit
fn, *args, **kwargs)
File "/usr/local/lib/python3.7/site-packages/joblib/externals/loky/process_executor.py", line 1022, in submit
raise self._flags.broken
joblib.externals.loky.process_executor.TerminatedWorkerError: A worker process managed by the executor was unexpectedly terminated. This could be caused by a segmentation fault while calling the function or by an excessive memory usage causing the Operating System to kill the worker. The exit codes of the workers are {EXIT(1), EXIT(1), EXIT(1), EXIT(1)}
対処法1: threadsなら動く
from joblib import Parallel, delayed
sum_list = Parallel(n_jobs=-1, prefer='threads')( [delayed(calc_sum)(i, j) for j in range(length) for i in range(length)])
- もちろん、処理によってはthreadsにしてしまうと遅くなってしまうこともある。
対象法2: multiprocessingにする
import multiprocessing
from multiprocessing import Process
with multiprocessing.Pool() as pool:
process = [pool.apply_async(calc_sum, (i, j)) for j in range(length) for i in range(length)]
sum_list = [f.get() for f in process]
対処法3: uWSGIではなく、guicornにする
私は、今回この方法をとりませんでした。
理由は、すでにuWSGIで稼働していたためAPサーバを変更するのはリスクがでかいためです。
最後に
速度比較
通常 | joblib(multiprocess) | joblib(threads) | multiprocessing |
---|---|---|---|
32.9 µs | 11 µs | 40.1 µs | 4.05 µs |
joblibの方が可読性はよく、書きやすいのですがmultiprocessingの方がPython本体に組み込まれている機能のため、性能が出ることも多いです。
また、今回みたいなエラーも踏むこともなさそうです。