LoginSignup
5
1

More than 3 years have passed since last update.

Python joblibの並列処理はuWSGI環境だと動かない。uWSGI上で並列処理するには?

Last updated at Posted at 2019-12-02

はじめに

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本体に組み込まれている機能のため、性能が出ることも多いです。
また、今回みたいなエラーも踏むこともなさそうです。

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1