Python は使いやすいプログラミング言語ですが、実行速度はお世辞にも速いとは言えません。時間がかかる処理をC言語で記述してライブラリを作成し、それをインポートして呼び出す方法もありますが、Python のプログラムを変更なして高速化できるならば、その方が簡単で便利です。今回は Python のプログラムを高速化する簡単な方法を 2 つ紹介します。
Python は Guido van Rossum 氏がC言語で作成した処理系 (CPython) が標準ですが、これ以外にもいくつか処理系があります。その中で JIT コンパイル機能を持つ PyPy は CPython の数倍速いといわれています。
PyPy は上記公式サイトからダウンロードすることができます。また、少し古いバージョンになりますが、Ubuntu 20.04 LTS の場合、次のコマンドでインストールすることができます。
$ sudo apt install pypy3 $ pypy3 --version Python 3.6.9 (7.3.1+dfsg-4, Apr 22 2020, 05:15:29) [PyPy 7.3.1 with GCC 9.3.0]
PyPy はどのくらい速いのか、たらいまわし関数を使って調べてみました。プログラムは次のようになります。
リスト : たらいまわし関数 (tarai.py) import time def tarai(x, y, z): if x <= y: return y return tarai(tarai(x - 1, y, z), tarai(y - 1, z, x), tarai(z - 1, x, y)) def tak(x, y, z): if x <= y: return z return tak(tak(x - 1, y, z), tak(y - 1, z, x), tak(z - 1, x, y)) def test(func, x, y, z): s = time.time() print(func(x, y, z)) e = time.time() print(e - s)
それでは実行結果を示します。実行環境は Ubuntu 20.04 LTS (WSL1), Intel Core i5-6200U 2.30GHz です。
$ python3 Python 3.8.10 (default, Jun 22 2022, 20:18:18) [GCC 9.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from tarai import * >>> test(tarai, 14, 7, 0) 14 56.31682109832764 >>> test(tak, 22, 11, 0) 11 61.39163279533386
$ pypy3 Python 3.6.9 (7.3.1+dfsg-4, Apr 22 2020, 05:15:29) [PyPy 7.3.1 with GCC 9.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>>> from tarai import * >>>> test(tarai, 14, 7, 0) 14 17.607698678970337 >>>> test(tak, 22, 11, 0) 11 21.849809169769287
PyPy の実行速度は CPython の約 3 倍になりました。プログラムによってはもっと速くなる場合もあるでしょうし、逆に遅くなる場合もあるかもしれません。たとえば、フィボナッチ関数の実行速度は次のようになりました。
リスト : フィボナッチ関数 (tarai.py に追加) def fibo(n): if n < 2: return n else: return fibo(n - 1) + fibo(n - 2) def testfibo(n): s = time.time() print(fibo(n)) e = time.time() print(e - s)
$ python3 Python 3.8.10 (default, Jun 22 2022, 20:18:18) [GCC 9.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from tarai import * >>> testfibo(38) 39088169 11.766613960266113
$ pypy3 Python 3.6.9 (7.3.1+dfsg-4, Apr 22 2020, 05:15:29) [PyPy 7.3.1 with GCC 9.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>>> from tarai import * >>>> testfibo(38) 39088169 1.1087980270385742
最新の PyPy を使うともう少し速くなるかもしれません。興味のある方はいろいろ試してみてください。
PyPy のほかにも、Python のプログラムを JIT コンパイルする方法があります。ライブラリ Numba をインポートすると Python に JIT コンパイラを導入することができます。
Numba のインストールは pip を使うと簡単です。pip は Python で書かれたパッケージ管理ツールです。次のコマンドで pip がインストールされているか確認することができます。
$ python3 -m pip -V /usr/bin/python3: No module named pip
pip が入っていない場合は、次のコマンドでインストールしてください。
sudo apt install python3-pip
あとは pip で Numba をインストールするだけです。
$ pip3 install numba
Numba の基本的な使い方は簡単で、高速化したい関数の前に @jit を付けるだけです。これでその関数が JIT コンパイルされます。それでは、たらいまわし関数で実行時間を計測してみましょう。
リスト : たらいまわし関数 from numba import jit import time @jit def tarai(x, y, z): if x <= y: return y return tarai(tarai(x - 1, y, z), tarai(y - 1, z, x), tarai(z - 1, x, y)) @jit def tak(x, y, z): if x <= y: return z return tak(tak(x - 1, y, z), tak(y - 1, z, x), tak(z - 1, x, y)) @jit def fibo(n): if n < 2: return n else: return fibo(n - 1) + fibo(n - 2) def test(func, x, y, z): s = time.time() print(func(x, y, z)) e = time.time() print(e - s) def testfibo(n): s = time.time() print(fibo(n)) e = time.time() print(e - s)
$ python3 Python 3.8.10 (default, Jun 22 2022, 20:18:18) [GCC 9.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from tarai import * >>> test(tarai, 14, 7, 0) 14 3.2922072410583496 >>> test(tarai, 14, 7, 0) 14 2.9705183506011963 >>> test(tarai, 14, 7, 0) 14 2.9666898250579834 >>> test(tak, 22, 11, 0) 11 3.4130196571350098 >>> test(tak, 22, 11, 0) 11 3.277550220489502 >>> test(tak, 22, 11, 0) 11 3.268494129180908 >>> testfibo(38) 39088169 0.545701265335083 >>> testfibo(38) 39088169 0.4606921672821045 >>> testfibo(38) 39088169 0.46005892753601074 実行環境 : Ubuntu 20.04 LTS (WSL1), Intel Core i5-6200U 2.30GHz
素の Python3 では tak(22, 11, 0) に 61.4 秒、PyPy3 では 21.8 秒かかりますが、Numba を使用することで 3.3 秒に短縮することができました。初回の実行にはちょっと時間がかかりますが、2 回目以降はどの関数も高速に実行することができました。
JIT コンパイラを使ったスクリプト言語では Julia が速いのですが、たらいまわし関数を実行すると約 2 秒ほどかかります。Julia に迫る速度を叩き出すのですから、Numba の JIT コンパイラは優秀ですね。Python のプログラムを高速化するときには試してみたいツールだと思ました。