最近、仕事でPyPy3を試したりしているので、メモを書き残しておく。
PyPy - Welcome to PyPy
インストール
今回はUbuntu 14.04LTSで試した。PyPy3はPPAリポジトリに無いので、Linux向けのコンパイル済みバイナリをダウンロードして使うことにした。
利用するときは、virtualenvでpypy3用の環境を作っている。
$ wget https://bitbucket.org/pypy/pypy/downloads/pypy3-2.4.0-linux64.tar.bz2
$ bzip2 -dc pypy3-2.4.0-linux64.tar.bz2|tar xf -
$ sudo mv pypy3-2.4.0-linux64 /opt/
$ sudo ln -s /opt/pypy3-2.4.0-linux64/bin/pypy3 /usr/bin/pypy3
virtualenvで利用する際には --python=pypy3 と指定する。
$ virtualenv --python=pypy3 venv-pypy
互換性について
PyPy3 2.4.0は、CPython 3.2.5互換とのことなので、CPython 3.3以降で導入されたシンタックスが使えないところは気をつけないといけない。
例えば、文字列リテラルに「u」をつけれない点。だたし、これは最初からPython3向けで書いていたコードならそもそも「u」は不要だし、気にしなくてもよさそう。
C言語で書かれたサードパーティ製のモジュールは動かないものが多いので、その場合はPure Pythonのもので代用する必要がある。
今回PyPyを適用したコードは、Python3.4向けに書いていたものであったが、アプリケーションロジックは一行も変更せずに動かすことができた。
互換性は高いと思う。
PyPyを適用するにあたって一点だけハマったところがあった。
「virtualenvでpypy3を動かした際に、出力のリダイレクトを指定するとエンコーディングがasciiになってしまう」というもの。
(venv-pypy)tokibito@ubuntu:~$ python -V
Python 3.2.5 (b2091e973da6, Oct 19 2014, 18:29:55)
[PyPy 2.4.0 with GCC 4.6.3]
(venv-pypy)tokibito@ubuntu:~$ cat test.py
import sys
sys.stdout.write(sys.stdout.encoding + '\n')
sys.stdout.write("日本語\n")
(venv-pypy)tokibito@ubuntu:~$ python test.py > hoge
Traceback (most recent call last):
File "test.py", line 4, in <module>
sys.stdout.write("日本語\n")
File "/home/tokibito/venv-pypy/lib-python/3/encodings/ascii.py", line 22, in encode
return codecs.ascii_encode(input, self.errors)[0]
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)
(venv-pypy)tokibito@ubuntu:~$ cat hoge
ascii
環境変数のPYTHONIOENCODINGにUTF-8を指定することで回避はできたけど、原因は特定できていない。
virtualenvを使わないでpypy3やpython3.4コマンドで実行すると、特に問題は発生しなかった。
パフォーマンスについて
今回PyPyを適用したコードは、ボトルネックが「2重ループ内での巨大なリストに対する値の参照と比較」となっていたバッチ処理。
データベースの参照やファイル読み込みについては、ループに入る前にすべて読み込んでキャッシュ済みの状態。
データベース接続と問い合わせはDjangoフレームワークのものを使っている。(Django1.8+pymysql)
PyPyを試す前に、cProfileを使って明らかに遅い処理は修正済みの状態。
Python3.4で実行した場合におよそ30分かかっていたもの。
(venv)$ time python manage.py my_script # Python 3.4の場合
real 30m34.483s
user 30m34.061s
sys 0m0.373s
これをPyPy3で動かした結果、6分程度で終了した。
(venv-pypy)$ time python manage.py my_script # PyPy3 2.4.0の場合
real 6m37.536s
user 6m37.238s
sys 0m0.289s
この他、同様に600分程度かかっていたバッチ処理が150分程度に短縮されるなど、CPythonの速度がネックになっていた処理はかなり速くなった。
雑感
- 速い
- Pure Pythonのサードパーティ製モジュールしか使っていないなら、すんなり動く可能性が高い
- ループ内で単純な計算、比較処理が多いものは速くなる可能性が高そう
- バッチ処理などは、設計段階でCモジュールに依存する処理を別のコマンドに分離しておくことで、部分的にPyPyを適用できてよかった
PyPyすごかった。