LoginSignup
0
0

More than 3 years have passed since last update.

alpineでC言語依存モジュールを pip install した時の時間を計測してみた

Posted at

Let's 計測

alpineでC言語依存モジュールを pip install すると激重になる話 のおまけ

素直にpip installした場合に、どれくらい時間がかかるか試してみた。
手元にあった機械学習用のrequirements.txtで実行してみると以下のようなログがでてきたので

Building wheels for collected packages: backcall, h5py, kiwisolver, matplotlib, numpy, pandas, pathspec, Pillow, PyYAML, pyzmq, regex, retrying, scikit-learn, scipy, tornado, typed-ast

開発時にも使用頻度高そうなモジュールだけピックアップして計測することにした。

  • 実行してみるリスト
    • numpy
    • pandas
    • Pillow
    • scipy
    • scikit-learn
    • h5py
    • matplotlib
    • regex

実験環境

手元で実行できる環境が二種類あったので両方とも試してみる。

  • case1: MacBookAir 2015 13inch intel-corei5 8GB DDR3 ・ docker desktop
  • case2: AMD Ryzen9 3900X, M.2 ASM2NE6500GTTD, 8x2GB DDR4 ・ ubuntu18.04(WSL2 integration)

計測方法

timeコマンドを頭につける事で実行時間を計測する。
どちらのケースも同一のdockerfileで実行しており、RUN命令文中にtime pip install hogeを埋め込む形を取っている。


FROM python:3.8-alpine3.11
RUN apk update \
  && apk add --virtual .build --no-cache openblas-dev lapack-dev freetype-dev \
  gfortran libxml2 g++ gcc zip unzip cmake make \
  libpng-dev openssl-dev musl libffi-dev python3-dev libxslt-dev \
  libxml2-dev jpeg-dev \
  && apk add --no-cache -X http://dl-cdn.alpinelinux.org/alpine/edge/testing hdf5-dev

RUN pip install --upgrade --no-cache-dir pip setuptools wheel && \
  time pip install --no-cache-dir Cython && \
...

こんな感じ。
docker build中に出力されるログをまとめたのが次の表となる。

結果

※ Cythonはalpineでもコンパイル不要のモジュールであり、Cython-0.29.16-py2.py3-none-any.whlのような.whl形式で降ってきます。非依存モジュールとの比較としてここに載せています。1

module / time case1 case2
real user sys real user sys
Cython 3.07s 2.33s 0.42s 1.52s 0.99s 0.07s
numpy 5m 28.27s 7m 6.90s 22.11s 2m 11.15s 3m 23.69s 6.05s
pandas 31m 46.58s 30m 36.25s 0m 53.83s 14m 8.24s 13m 53.10s 15.88s
Pillow 50.81s 44.09s 5.99s 24.79s 19.88s 1.69s
scipy 30m 45.99s 36m 29.33s 1m 45.89s 12m 52.81s 17m 54.87s 42.58s
scikit-learn 14m 38.63s 14m 3.37s 28.98s 6m 33.10s 6m 24.84s 10.11s
h5py 3m 45.58s 3m 34.79s 9.30s 1m 45.87s 1m 42.42s 4.18s
matplotlib 2m 51.50s 2m 35.53s 13.59s 1m 21.77s 1m 13.70s 6.75s
regex 30.52s 28.84s 1.24s 13.75s 13.06s 0.34s

おっっっっそい
ものっっっっっそい、おっっっっっっそい

case1だとnumpyだけでも5分かかるし、pandas・scipyに至っては30分を超えている。scikit-learnに至ってはnumpy・scipyに依存しているので実際のところ一時間は覚悟しないといけない。
挙げた全部合わせると一時間半超だ。
これがどういう事になるか。一日8時間作業すると仮定すると

一日たったの5回程度しかデプロイする事ができないことになる

気軽に開発環境に最新ブランチを充てたり、軽微なバグ修正の確認すら困難になる。作業時間も含めると更に減る。

一応、メモリが倍になると処理速度も倍になるので時間の短縮はできなくもない。
awsを使ったりしているのならメモリ量が多いインスタンスタイプを選択することで時短は可能だ。
しかし、性能的にcase1の二倍以上のcase2だと半分以下の時間で済むとはいえ、全部で40分ほどかかっているのは事実。
一日あたり最高で12回ほどのデプロイしかできない。


ちなみに、表の上から順番にpip installしていったので一部モジュールの実行時間には依存モジュールのインストール実行時間も含まれる。
kiwisolverもtar.gzで降ってきているようなのでmatplotlib単体はもう少し早いはず。

  Successfully built pandas
  Installing collected packages: six, python-dateutil, pytz, pandas
  Successfully installed pandas-0.25.3 python-dateutil-2.8.1 pytz-2019.3 six-1.14.0
------
  Successfully built scikit-learn
  Installing collected packages: joblib, scikit-learn
  Successfully installed joblib-0.14.1 scikit-learn-0.22.2.post1
-----
  Successfully built matplotlib kiwisolver
  Installing collected packages: cycler, kiwisolver, pyparsing, matplotlib
  Successfully installed cycler-0.10.0 kiwisolver-1.2.0 matplotlib-3.2.1 

まとめ

軽いものでも1個あたり1~2分かかるレベルなので、なるべくC言語依存モジュールはビルドしないで済む運用を考える必要がある。condaを使う、は今無しで

余談pip

pip install時はモジュールのwheel化が完了した直後に、再度whlを展開し直して必要な生成物をまるっとsite-package直下に移動する。pythonで書かれたコードからビルドされた共有ライブラリ郡*.soも含めて全てだ。
alpineのようにpython3.8のバイナリが/usr/local/bin直下にいるとすると/usr/local/lib/python3.8/site-packages/に移動する。

再展開は手間のように見えるがこれはPEPに則った安全なモジュールの導入法である。PEP491にもそう書かれている2


試しに、ビルド済みのwhlファイルをunzipして.soファイルを検索してみる。

./numpy/linalg/lapack_lite.cpython-38-x86_64-linux-gnu.so
./numpy/linalg/_umath_linalg.cpython-38-x86_64-linux-gnu.so
./numpy/core/_operand_flag_tests.cpython-38-x86_64-linux-gnu.so
...

それぞれのwhlファイルをpip installで導入後にライブラリパスに対して.soファイルを検索してみると、モジュール毎にビルド済みファイルが存在しているのを確認できる。

/usr/local/lib/python3.8/site-packages/numpy/linalg/lapack_lite.cpython-38-x86_64-linux-gnu.so
/usr/local/lib/python3.8/site-packages/numpy/linalg/_umath_linalg.cpython-38-x86_64-linux-gnu.so
/usr/local/lib/python3.8/site-packages/numpy/core/_operand_flag_tests.cpython-38-x86_64-linux-gnu.so
...
/usr/local/lib/python3.8/site-packages/pandas/io/sas/_sas.cpython-38-x86_64-linux-gnu.so
/usr/local/lib/python3.8/site-packages/pandas/_libs/hashtable.cpython-38-x86_64-linux-gnu.so
...

これでようやくpythonでimportできる訳だが、
裏を返せばpythonがモジュールを検索するパスに生成物さえ居れば使用できる。
なので元記事の回避法では、python関連以外は後に入れること前提で/usr/local/直下をdockerのマルチステージビルドを使ってえいやでコピペしている。

参考文献


  1. ここに書いてあるように、none-anyは非OS・アーキテクチャ依存のモジュールでほとんどの場合pureなpythonで作成されています。公式PEP425も参照。 

  2. 余談中の余談だがanacondaは独自のモジュールインストール方法を採っているのでPEPに即していない。混ぜるな危険を引き起こしている一端。 

0
0
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
0
0