背景
- 複雑・大規模なリソース配分問題を手作業で解いて苦労されている方々がいらっしゃったので、Pulp + CBCを使った最適化アプリを提供することにした。
- しかしノンプログラマの方々なので、コマンドラインでPythonを実行したりpipでパッケージを入れたりするのはハードルが高いかなと思った。
- そこで、Pyinstallerでスタンドアロンなexeファイルを作り、ワンクリックで実行できるようにしようと思った。1
環境
- Anaconda 4.4.0 (Python 3.6)、 Pyinstaller 3.3.1.
- OSはWindows 7, 10とかMacOSとかいろいろ試した。
問題
- numpy, pandas, scipyなどをimportした上で
pyinsaller hogehoge.py --onefile
すると…- ファイルサイズが200--600MBと巨大になる。
- 生成したexeファイルを実行してもエラーがでる。
原因
- Anacondaを使っていることが主な原因。
- より正確には、Anacondaに入っているIntel MKL (Intel CPUのアーキテクチャに特化した線形演算ライブラリ) が原因。MKL関係をバンドルしようとしてファイルサイズが大きくなったりエラーが出たりしている。
解決方法その1
- numpyとかpandasとかそんな便利パッケージは使わず自分でゴリゴリ書く。
解決方法その2 (MKLが必要ない場合。お手軽。)
- 今回の私のケースでは計算速度のボトルネックはcbcであり、pythonでの線形演算は遅くても構わなかった(=MKLが必要なかった)ので、この解決法を取った。
condaを使って、MKLの入っていない普通のpython仮想環境を作る。
$ conda create -n fugafuga python=3.6 # 仮想環境の作成
$ activate fugafuga # 仮想環境のアクティベート
$ pip install numpy pandas scipy pulp xlsxwriter # お好きなパッケージをどうぞ
$ pip install pyinstaller pypiwin32 # Windowsでpyinstallerを動かすにはpypiwin32が必要らしい
あとは普通にpyinstallerを使うだけ。ただしpandasを使う場合pandas._libs.tslibs.timedeltas
がうまくimportされないようなので、hidden-import
に明示する必要がある。
$ pyinstaller hogehoge.py --onefile --hidden-import='pandas._libs.tslibs.timedeltas'
これで生成されるexeファイルは30MBくらいに収まった。
[おまけ] 解決方法その3? (MKLを使いたい場合)
- 線形演算の速度がボトルネックの場合は、ファイルサイズが大きくなってでもMKLをバンドルしたほうがよさそう。
- とりあえず以下のようにすれば動く。(性能のベンチマークは取っていないのでご自身で確認を。)
$ pyinstaller hogehoge.py --onefile --hidden-import='packaging' --hidden-import='packaging.version' --hidden-import='packaging.specifiers' --hidden-import='packaging.requirement'
- Warningもたくさんでるので中身を確認したほうが良さそう。たとえば私の場合は
libtbb
が見つからないというWarningが出ていた。これはIntel threading building blocks (Intel TBB)のことで並列演算用の動的ライブラリらしい。ライブラリ自体はIntelのページからダウンロードできるのでpyinstallerのドキュメントを読んでうまく紐付けるなどの対応が必要だと思う。
その他
- Windowsで生成したexeファイルを配布先で実行するときに
api-ms-win-crt-runtime-l1-1-0.dll
が見つからないと怒られることがある。これはMicrosoft Visual C++ 2015 Redistributableに含まれているので、さくっとインストールしてもらうと楽。(頑張ればpyinstallerでバンドルできるのかもしれないが。)
参考
- https://github.com/pyinstaller/pyinstaller/issues/2270#issuecomment-384074743
- https://github.com/pyinstaller/pyinstaller/issues/1566#issuecomment-144935067
-
もっと楽な方法があったら教えてください。 ↩