の目的のため、高速化アプローチについて概観するためのメモを作成する。
想定する読者
python の中級者
pypy, Numba などの上級者向けのことは書いてありません。
自作モジュールは必要なのか
- その分野で使われだしているOSSのモジュールはないか?
- 車輪の再発明をしなくてよい。
- OSSのモジュールで標準化されていれば、次の作業は不要になる可能性が高い
- CPU, GPUのサポート
- マルチコアを使った高速化のサポート
- 自分たちが着目している分野のOSSでどういうライブラリが使われているのかを確認しよう。
- そのライブラリを使えば、自作ライブラリを書く量が減る。
- 既存のライブラリに1つ機能を追加したほうが、全体がすっきりする。
- 既存のgithubのリポジトリをforkして実装を作り、動作検証後のものを本家にPRを上げる。
- (そうすれば、自分がメンテナンスし続けなければならないライブラリが減る。)
以下は自作モジュールが必要な前提で書く。
処理時間を計測する
- 例 cv2.getTickCount() を使う
timeit
timeit --- 小さなコードスニペットの実行時間計測
Pythonのtimeitモジュールで処理時間を計測
cProfile で処理時間の内訳を見よう。
-m cProfile -s cumtime
を追加するだけ
- 関数の呼び出し回数は妥当ですか?
例:画像の正規化処理などを無駄に繰り返している。
python3 -m cProfile -s cumtime myprog.py
画像処理・画像認識の場合には、毎回の処理時間の方が重要
- 初期化処理
- 毎回の処理
- 終了処理
これらのうち、画像処理・画像認識の場合には、毎回の処理時間の方が重要。
最近の機械学習の分野では、ひっそりとデータファイルを自動でダウンロードしていることがあります。
- 毎回のプログラムの実行の中でダウンロードするのではなく、環境構築の際にデータをダウンロードするように書き換えてはどうでしょう。
無駄に遅くしているもの
-
matplotlib での描画
-
学習済みのモデルファイルのダウンロード
- 環境構築の際に一度だけ実行すればよいように書き換える。
-
学習済みのモデルファイルのtensorRTへの書き換えを立ち上げに毎回行なってしまう
- 環境構築の際に一度だけ実行すればよいように書き換える。
-
複数人が利用する場合には変換済みのものをダウンロードする。
-
for 文を使って画素毎に処理しているコード
numpyで明示的にループを書くと極端に遅くなる
画像処理で2重ループをなるべく書くな
- generator で十分なところをlistにするな。
for p in glob.glob("**/*.jpg"):
print(p)
ならば、jpgファイルを全数探し終わってなくても、表示を開始する。
for p in sorted(glob.glob("**/*.jpg")):
print(p)
とすると、全数探して、並び替えを完了してからでないと表示が始まらない。
- list.extend()で十分なところを+で代入をし直すな。
悪い例
a = [1, 2, 3]
a = a + [4, 5, 6]
良い例
a = [1, 2, 3]
a.extend([4, 5, 6])
さらには、配列の大きさが予めわかっている場合には、最初にnp.zeros(shape, dtype=np.float)
などとして領域を確保してから、
該当の行を代入していくということで、領域の確保に関する処理時間を減らせることがあります。
-
Pythonはなにも考えないと、シングルスレッド・シングルコアで動作する。
-
multiスレッドで動くライブラリを使う
threading --- スレッドベースの並列処理
Trio – a friendly Python library for async concurrency and I/O -
Cythonを使う
Cython ―Cとの融合によるPythonの高速化 -
C++の数値計算ライブラリをOpenMPのpragmaを使って書き換える。
並行コンピューティング技法 ―実践マルチコア/マルチスレッドプログラミング
#pragma omp parallel
をキーワードに検索してみてください。
-
numpyでの実装をpytorchでの実装に書き換えて、GPUを使っての動作を高速化させる。
ChatGPTでも次の質問に答えて例を書いてくれます。
「numpy をpytorchに書き換える例を示してください。」 -
CPU-GPU 間のメモリ転送を減らす。
-
deploy 環境に合わせたモデルに変換する。
例: pytorch -> TensorRT
例: pyTorch -> onnx
数値データを保存する際にバイナリモードの形式があれば利用する。
数値データをasciiデータファイルで保存し、
asciiデータファイルを数値データとして読み込むのは
処理が重くなります。
対象のデータファイルにバイナリモードがあれば、
バイナリデータでデータを保存し、それを読み込むようにします。
例:点群データのply ファイルにもASCIIとバイナリの2つのバージョンが存在します。
利用するツールやライブラリがバイナリモードがあれば、
その分、処理が速くなります。
opencv-python のbuild 条件を確認する。
OpenCVのモジュールは、build 条件によって性能が著しく変化します。
CPUの種類に応じた最適化が入っているか
Video I/O: はどうなっているのか
などです。
GStreamer:
を使いたければ、CMakeでGStreamerを有効化する設定にして、cmake, make しなければなりません。
必ず使うとは限らないライブラリのimportは後回しにする。
スクリプトの動作改善するためには、
必ず使うとは限らないライブラリのimportは後回しにするのも手だ。
付記: docker build のキャッシュが有効になるDockerfileの記述順序
Dockerfile の記述のうち、変更を生じやすいものを後ろにする。
変更を生じない部分は、前回のdocker build の結果が利用されるので、変更部分以降しか処理されない。