はじめに
Pythonスクリプトを他人に渡したいとき、相手のPCでも環境構築するのは手間です。可能なら実行ファイルにしたいと思います。それを実現する便利なモジュールとして、PyinstallerやNuitkaがあります。後者はCに変換してからコンパイルを行い、実行ファイルの容量が小さい、実行速度が速いといった特徴があるそうです。
Nuitkaはコンパイルした後、実行してImportErrorが出たら修正するなど面倒なことがあります(Pyinstallerも)。そこでNUITKA-Utilitiesのhinted-compilationを用いて、Pythonスクリプトを一度走らせて使用したモジュールを把握してからコンパイルすることで失敗が減ります。とても簡単に使えて便利だったのでPyinstallerとの比較を交えて紹介したいと思います。
参考
こちらでNUITKA-Utilitiesを知りました。hinted-compilation以外のスクリプトの使い方はこちらをご参考にしてください。
Qiita:NUITKA-Utilitiesで簡単Pythonコンパイル
1. NUITKA-Utilitiesの準備
NUITKA-Utilitiesをダウンロードします。GitHub:NUITKA-Utilities
予めnuitka, pysimplegui, MinGWのインストールを行います。Anaconda環境の場合は、以下の通りです。
conda install -c conda-forge nuitka
conda install -c conda-forge pysimplegui
MinGWのインストールは少し注意が必要で、インストール済みのPythonのアーキテクチャと合わせる必要があります。コマンドプロンプトにPythonと打ち込んで確認します。
C:\>python
Python 3.7.4 (default, Aug 9 2019, 18:34:13) [MSC v.1915 64 bit (AMD64)] :: Anaconda,
Inc. on win32
Let'sプログラミング:MinGW-w64のダウンロードとインストールを参考にMinGWをインストールします。
上記の例ではPythonが64bitなので、MinGWのインストーラのArchitectureをx86_x64に変更します。
PATHがちゃんと通っているか確認します。
C:\>gcc --version
gcc (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
別のバージョンがもともとあるなどでPATHを設定したくないときは、MinGWのインストール先のmingw-w64.batを実行すれば良いです。
2. NUITKA-Utilities hinted-compilationの使い方
説明のために、コンパイルしたいスクリプトファイルの名前をyourscript.pyとします。
-
NUITKA-Utilities\hinted-compilationディレクトリに移動して、以下のようにスクリプトを実行します。
スクリプトが実行され、使用されたモジュールがjsonファイルに書き出されます。python get-hints.py yourscript.py
-
続いて以下のようにスクリプトを実行します。このときyourscript.pyと先程のjsonファイルは同じ階層に置きます。
python nuitka-hints.py yourscript.py
これだけでPythonスクリプトファイルから実行ファイルが作れます。ファイルを一つにまとめたい場合は、onefile-maker-windows.pyを使用します。
3. ファイル容量、実行速度・起動速度の比較
コード
SPI3で出てきた問題を題材にコードを書きました。これを例に実行速度と起動速度を計測します。
import numpy as np
import time
def coin_prob(N=1000000):
"""
10円1枚、5円玉2枚、1円5枚を投げて、
表が出たコインの金額を足す。
15円になる確率はいくらか。
"""
coins = np.array([10,5,5,1,1,1,1,1])
count=0
for _ in range(N):
heads_or_tails = np.random.randint(0,2,len(coins)) #0か1
if np.sum(coins*heads_or_tails) == 15:
count += 1
print("確率は{}".format(count/N))
def main():
"""
10回の平均時間と標準偏差を求める。
"""
elapsed_times = []
for _ in range(10):
start = time.time()
coin_prob()
end = time.time()
elapsed_times.append(end-start)
ave_time = np.average(elapsed_times)
std_time = np.std(elapsed_times)
print("処理時間={0}±{1}".format(ave_time,std_time))
if __name__ == "__main__":
start_all = time.time()
main()
end_all = time.time()
print("総経過時間{}".format(end_all-start_all))
Pyinstaller、Nuitka双方とも一つのファイルにまとめるオプションはつけていません。
余談ですが、Pyinstaller、Nuitkaともにすんなり行かず、少し苦しみました。
Pyinstallerはこちらの記事を参考に解決しました。
Qiita:Pyinstallerを使ってPythonコードから生成した実行ファイルについて、実行時エラーModuleNotFoundErrorを回避
Nuitkaはmkl関係でうまく行かずこちらで解決。
ぬうぱんの備忘録:conda 環境下で mkl のロードに失敗するときの対処法あれこれ
ファイル容量
はじめにNuitkaはファイル容量が小さいと言いましたが、そうでもありませんでした。
これから改良が続いて、余計なモジュールをインポートしなくなるといいですね。
Original | Pyinstaller | Nuitka |
---|---|---|
1.02KB | 569MB | 623MB |
実行速度
コイン投げ試行1セットの処理時間として、10回の平均と標準偏差を示します。
本旨に外れますが、Numba jitも比較に入れました。高速化を求めるならこちらを使ったほうが良いです。
Original | Pyinstaller | Nuitka | Numba jit | |
---|---|---|---|---|
Time s | 18.44±0.54 | 18.30±0.26 | 4.275±0.076 | 0.2299±0.1281 |
Gain | 1.000 | 1.008 | 4.314 | 80.21 |
起動速度
Windows Server 2003 Resource Kit Toolsのtimeit.exeでプログラムの起動から終了までの時間を計測し、プログラム内部でのmain関数の処理時間との差として、3回計測を行いました。
Original | Pyinstaller | Nuitka | |
---|---|---|---|
Time ms | 213.3±2.8 | 235.8±2.2 | 88.39±1.72 |
Gain | 1.000 | 0.905 | 2.414 |
まとめ
ファイル容量を除いて、実行速度・起動速度の点では、Pyinstallerより優れた性能がでました。Pyinstallerよりはエラーが少なく、簡単なのでおすすめです。