python コード変換無しで高速化

はじめに

pythonはコンパイル言語に比べ、かなり遅いですが、開発の時間は短縮できると思います。(思いついたらすぐにプログラムに起こせる)
でも使ってると遅いので、numbaやら、cythonやメモ化などを利用して高速化を行うと思います。pythonコードを一切変更せずに高速化できるpypyについて紹介したいと思います。

pypyとは?

http://pypy.org/features.html#stackless

目的

cythonより速く動作することを目標とし作成されています。基本的に、cythonより速く動作します。

速度

速度を上げるために、実行時コンパイルを実装しています。(しかし、動的解析が終わる前にプログラムが終了するのであれば、実行時コンパイルがオーバーヘッドになります。なので、高速化にはプログラムは数秒間実行される必要があります。また、プログラムがpythonコードではなく、ランタイムライブラリに時間がかかる場合、高速化は望めません)
よって、pypyは、Pythonコードの実行にかなりの時間が費やされる長時間実行プログラムを実行するとき最大の高速化を行います。

メモリ使用量

基本的にpythonよりメモリを使用しません。(注意事項として、windowsはpypy 64bitが配布されていません。大規模領域のメモリを使用する場合はunixが必要)

その他いろいろ...

pypyのダウンロード

以下のサイトに、pypyがあります。

http://pypy.org/download.html

windowsのみ、32bitです。今回はwindowsをダウンロードします。

展開したフォルダで、「shift + 右クリック」を行い「PowerShellウインドウをここに開く」を選びます。

.\pypy3.exe コマンドを実行するとpypyのインタラクティブシェルは起動します。(初回起動時、ウイルススキャンにより実行開始が遅い場合があります)

PS D:\Users\PCUser\Downloads\pypy3-v6.0.0-win32> .\pypy3.exe
Python 3.5.3 (fdd60ed87e94, Apr 24 2018, 06:27:13)
[PyPy 6.0.0 with MSC v.1910 32 bit] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>>

exit()やctrl+c で終了します。

pipのインストール

pypyは展開されたフォルダ内に環境を作成します。なので、アナコンダや、すでにpythonがある環境とは別です。pythonモジュールをインストールするpipをpypyで利用できるようにします。次のコマンドを実行します。

PS D:\Users\PCUser\Downloads\pypy3-v6.0.0-win32> .\pypy3.exe -m ensurepip
Collecting setuptools
Collecting pip
Installing collected packages: setuptools, pip
Successfully installed pip-9.0.1 setuptools-28.8.0
PS D:\Users\PCUser\Downloads\pypy3-v6.0.0-win32> .\pypy3.exe -m pip install -U pip
Cache entry deserialization failed, entry ignored
Collecting pip
  Cache entry deserialization failed, entry ignored
  Downloading https://files.pythonhosted.org/packages/c2/d7/90f34cb0d83a6c5631cf71dfe64cc1054598c843a92b400e55675cc2ac37/pip-18.1-py2.py3-none-any.whl (1.3MB)
    100% |################################| 1.3MB 644kB/s
Installing collected packages: pip
  Found existing installation: pip 9.0.1
    Uninstalling pip-9.0.1:
      Successfully uninstalled pip-9.0.1
Successfully installed pip-18.1
Cache entry deserialization failed, entry ignored

先程も書きましたが、pypyは別環境です。

PS D:\Users\PCUser\Downloads\pypy3-v6.0.0-win32> pip -V
pip 18.1 from c:\users\pcuser\appdata\local\programs\python\python37\lib\site-packages\pip (python 3.7)
PS D:\Users\PCUser\Downloads\pypy3-v6.0.0-win32> .\pypy3.exe -m pip -V
pip 18.1 from D:\Users\PCUser\Downloads\pypy3-v6.0.0-win32\site-packages\pip (python 3.5)

モジュールのインストール

ではモジュールをインストールしてみます。ここではsimplejsonをインストールします。

PS D:\Users\PCUser\Downloads\pypy3-v6.0.0-win32> .\pypy3.exe -m pip install simplejson
Collecting simplejson
  Downloading https://files.pythonhosted.org/packages/e3/24/c35fb1c1c315fc0fffe61ea00d3f88e85469004713dab488dee4f35b0aff/simplejson-3.16.0.tar.gz (81kB)
    100% |################################| 81kB 1.4MB/s
Installing collected packages: simplejson
  Running setup.py install for simplejson ... done
Successfully installed simplejson-3.16.0

インストールできました

しかしながら、

「windows版だと、コンパイルが必要なモジュールは高確率で利用できません(pythonコードのみのモジュールしか使えない)」

ubuntuでは、numpyや、jupyterlabをpypyにインストールできたので、やっぱりwindowsはつかえなゲフンゲフン。

実際には?

次のプログラムを実行してみました。for と再帰の速度をテストします。

def hoge(N):
    foo=0
    for i in range(N):
        foo+=1
    return 0
def bar(N):
    for i in range(N):
        bar(N-1)
    
import cProfile
cProfile.run('hoge(10**7)')
cProfile.run('bar(10)')

python3.7.1

PS D:\Users\PCUser\Downloads\pypy3-v6.0.0-win32> python .\fibo.py
         4 function calls in 6.790 seconds
   Ordered by: standard name
   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    6.790    6.790 <string>:1(<module>)
        1    6.790    6.790    6.790    6.790 fibo.py:1(hoge)
        1    0.000    0.000    6.790    6.790 {built-in method builtins.exec}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
         9864104 function calls (4 primitive calls) in 4.581 seconds
   Ordered by: standard name
   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    4.581    4.581 <string>:1(<module>)
9864101/1    4.581    0.000    4.581    4.581 fibo.py:6(bar)
        1    0.000    0.000    4.581    4.581 {built-in method builtins.exec}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

pypy3( Python 3.5.3 32bit)

PPS D:\Users\PCUser\Downloads\pypy3-v6.0.0-win32> .\pypy3.exe .\fibo.py
         8 function calls in 0.170 seconds
   Ordered by: standard name
   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.170    0.170 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 ?:1(anonymous)
        1    0.170    0.170    0.170    0.170 fibo.py:1(hoge)
        1    0.000    0.000    0.170    0.170 {built-in function exec}
        2    0.000    0.000    0.000    0.000 {built-in function isinstance}
        1    0.000    0.000    0.000    0.000 {method 'bit_length' of 'int' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
         9864254 function calls (154 primitive calls) in 1.835 seconds
   Ordered by: standard name
   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
       75    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:190(cb)
        1    0.000    0.000    1.835    1.835 <string>:1(<module>)
9864101/1    1.835    0.000    1.835    1.835 fibo.py:6(bar)
        1    0.000    0.000    1.835    1.835 {built-in function exec}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
       75    0.000    0.000    0.000    0.000 {method 'get' of 'dict' objects}

pypyにインタプリタを変えただけで、for が40倍、再帰が2.5倍早くなりました。

ちなみに、nunbaはfor が0.057 seconds、再帰が0.105 secondsでした。

実際に動かすプログラムによって高速化の度合いが変わってくるので実際に試してみるしかないですね。