T.H/ 2020年 9月 18日/ 技術

ご挨拶

今回からブログの執筆に参加させていただきます、T.H.です。
技術的な経歴は.NETアプリケーションが一番長いです。Pythonはまだまだ勉強中です。
1回目として相応しいかはわかりませんが、Pythonについて個人的に驚いた内容です。

背景

Pythonでとある処理を高速化したいなーという機会がありまして、経歴上.NETアプリをメインにやっていた私はとりあえずの手段としてマルチスレッド(threading.Thread)に飛びつきました。

早くなったのか

なりませんでした。
Pythonには「Global Interpreter Lock, GIL」という仕組みがあります。
(https://docs.python.org/ja/3/glossary.html#term-global-interpreter-lock)
この機能により、基本的に同時に実行されるスレッドは1本のみになります。
そのため、残念ながらスレッドを何本起動しても高速化はされません。

解決方法

マルチプロセス「concurrent.futures.ProcessPoolExecutor()」を使用します。
(https://docs.python.org/ja/3.8/library/concurrent.futures.html)

import concurrent.futures
~~略~~
    def multi_processing():
        with concurrent.futures.ProcessPoolExecutor(max_workers=4) as excuter:
            day_values = list(excuter.map(do_something, data_list))

    def do_something(data):
        # 処理

excuter.map()の第1引数にメソッド、第2引数にメソッドに渡す引数のlistを指定する
点がポイントで、ProcessPoolExecutorのmax_workersで指定したワーカープロセスに自動で割り振ります。
max_workersはCPUコア数に合わせて指定するのが良いかと思います。
なお、プロセスを跨ぐ都合上、引数はpickle化可能でなければなりません。

最後に

おそらく多くの方が既にご存知かと思われる内容でしたが、
.NETを長くやっていた身からしますと、マルチスレッドが高速化に全く寄与しないというのはそれなりに衝撃的な事実でした。

おまけ

検証途中で「Django+uWSGI」環境下でマルチスレッドを動かしていたりもしました。
大概のケースでは、マルチプロセスか、asyncで処理するため需要はほとんどないかと思いますが、下記を設定することでマルチスレッド動作が可能になります。

uwsgi.iniに下記追加

  enable-threads  = true
  threads         = 4

参考

https://arakan-pgm-ai.hatenablog.com/entry/2019/05/02/000000

About T.H

North Torch株式会社 プログラマ 技術的な経歴は.NETアプリケーションが一番長い。 その他はまだまだ勉強中。