Python高速化手法の処理時間比較
Python関連で様々な高速化方法が存在しますが、どれがどのくらい速いの?ということを確認するため、実際に環境構築から処理時間測定までを行いました。
結果は以下になります。
実行方法 | 処理時間 [ms] | コメント | 評価方法 |
---|---|---|---|
python3.6.9 | 3375 | ・みんなが使うもの | リンク |
python3.6.9 (docker) | 4772 | ・dockerはちょっと遅い | リンク |
python2.7.17 | 3056 | ・3系に比べてなぜか速い | リンク |
Numba | 375 | ・プログラムの微修正で高速化可能 | リンク |
Cython | 151 | ・速い!! ・プログラムにいろいろ追記が必要 (ほとんどC言語なんじゃ・・・) | リンク |
PyPy3 | 728 | ・プログラム修正無しで動いた ・インストールがめんどう | リンク |
PyPy2 | 741 | ・こっちはapt-getでインストール可能 | リンク |
Julia | 211 | ・期待の新言語 ・pythonっぽい書き方だが速い ・インストールがめんどう | リンク |
C言語(-O2) | 151 | ・Cythonと同等の速度 | リンク |
C言語(-O3) | 150 | ・Cythonと同等の速度 | リンク |

処理時間測定には、ifや算術演算がループ処理の中に存在する素数判定アルゴリズムを用いました。2~10000までの素数の数をカウントするプログラムの処理時間を測定しています。
また、処理時間はすべて10回測定しその平均を記載しています。
- GCP(Google Cloud Platform)
- Ubuntu18.04 LTS
- マシンタイプ:N1シリーズ(n1-standard-1)
- CPU:Intel(R) Xeon(R) CPU @ 2.00GHz 1コア
- メモリサイズ:3.75GB
- ディスク:SSD
評価方法
python
インストール
python2系と3系のインストール方法です。Ubuntu18.04の場合既にインストール済みのはずです。
sudo apt-get update
sudo apt-get install python
sudo apt-get install python3
評価用プログラム
素数カウント用サンプルコードです。
python3_base.pyというファイル名で保存します。
(2系も3系も同じコードで動きます)
import time
def prime_jud(num):
ret = 1
for i in range(2, num):
if (num%i) == 0:
ret = 0
return ret
def prime_count(max_num):
cnt = 0
for i in range(2, max_num):
cnt += prime_jud(i)
return cnt
if __name__ == "__main__":
max_num = 10000
start = time.time()
ret = prime_count(max_num)
end = time.time()
print(ret)
print('time:'+str(end-start)+str(' sec'))
実行方法
2系の場合
python python3_base.py
3系の場合
python3 python3_base.py
結果(2系,3系共通)
1229
time:3.359515905380249 sec
2~10000までの素数が1229個存在しその処理に3.36秒かかったことを表しています。
Docker
インストール
pythonコマンドのDockerHubからのダウンロードとコンテナ化とバージョン確認を行います(欲張りすぎ・・・)。
sudo docker run -it --rm --name my-running-script -v "$PWD":/usr/src/myapp -w /usr/src/myapp python:3.6.9 python -V
下記が表示されると成功です。
Python 3.6.9
評価プログラム
実行方法
docker経由のpython実行コマンドです。
sudo docker run -it --rm --name my-running-script -v "$PWD":
/usr/src/myapp -w /usr/src/myapp python:3.6.9 python python3_base.py
下記の表示で成功です。
1229
time:4.709121465682983 sec
Numba
インストール
続いてNumbaになります。
pip3を入れていない人は下記で入ります。
sudo apt-get install python3-pip
Numba公式ページに従いインストールします。
pip3 install numba
pip3 install colorama==0.3.9
評価プログラム
python3_numba.pyというファイル名で保存します。
素のpython3プログラムとの違いはnumbdaをimportしているところと、関数先頭に、@numba.jitを記載している部分になります。
import numba
import time
@numba.jit
def prime_jud(num):
ret = 1
for i in range(2, num):
if (num%i) == 0:
ret = 0
return ret
def prime_count(max_num):
cnt = 0
for i in range(2, max_num):
cnt += prime_jud(i)
return cnt
if __name__ == "__main__":
max_num = 10000
start = time.time()
ret = prime_count(max_num)
end = time.time()
print(ret)
print('time:'+str(end-start)+str(' sec'))
実行方法
python3と同じです。
python3 python3_numba.py
下記実行結果になります。明らかに速くなっています。
1229
time:0.3827321529388428 sec
Cython
インストール
続いてCythonになります。
Cython公式ページに従いインストールします。
pip3 install cython
評価プログラム
Cythonプログラムは変数や関数戻り値の型を厳密の定義する必要があります。また拡張子も.pyから.pyxへ変更する必要があります。
このプログラムをpython3_cython.pyxとして保存します。
import cython
import time
cdef prime_jud(int num):
cdef:
int i, ret
ret = 1
for i in range(2, num):
if (num%i) == 0:
ret = 0
return ret
def prime_count(int max_num):
cdef:
int i, cnt
cnt = 0
for i in range(2, max_num):
cnt += prime_jud(i)
return cnt
pythonから呼び出す関数をdefとして定義し、Cプログラム内で利用する関数をcdefとして定義します。
python3_cython.pyxをコンパイルするためのプログラムを作成します。
ファイル名はsetup.pyとします。
from distutils.core import setup, Extension
from Cython.Build import cythonize
from numpy import get_include # cimport numpy を使うため
ext = Extension("python3_cython", sources=["python3_cython.pyx"], include_dirs=['.', get_include()])
setup(name="python3_cython", ext_modules=cythonize([ext]))
コンパイルした関数(prime_count)をpythonプログラムから呼び出す部分を作成します。
ファイル名はpython3_cython.pyとします。
import time
from python3_cython import prime_count
import python3_cython
if __name__ == "__main__":
max_num = 10000
start = time.time()
ret = prime_count(max_num)
end = time.time()
print(ret)
print('time:'+str(end-start)+str(' sec'))
実行方法
はじめにコンパイルを行います。
python3 setup.py build_ext --inplace
次に実行します。
python3 python3_cython.py
下記実行結果になります。ふー長かった。でも速い。
1229
time:0.15123748779296875 sec
PyPy
PyPy2系と3系でインストール方法が異なります。
インストール(PyPy2)
まずは2系です。
sudo apt-get update
sudo apt-get install pypy
バージョン確認
pypy -V
Python 2.7.13 (5.10.0+dfsg-3build2, Feb 06 2018, 18:37:50)
[PyPy 5.10.0 with GCC 7.3.0]
インストール(PyPy3)
PyPy公式Downloadページ⇒Python 3.6 compatible PyPy3.6 v7.3.1⇒Linux x86-64 binary (64bit, built on CentOS6)からビルド環境のパスを取得。

ここでは、wgetを用いて取得しますがブラウザ等で取得しても問題ありません。各自好きな場所に配置しtarで展開してください。
wget https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.3.1-linux64.tar.bz2
tar xvf pypy3.6-v7.3.1-linux64.tar.bz2
ビルド済みの環境のためそのまま実行可能です。
そのため、実行フォルダに対してPATHを通します。
echo "export PATH=~/pypy3.6-v7.3.1-linux64/bin:$PATH" >> ~/.bashrc
source .bashrc
バージョン確認
pypy3 -V
Python 3.6.9 (2ad108f17bdb, Apr 07 2020, 02:59:05)
[PyPy 7.3.1 with GCC 7.3.1 20180303 (Red Hat 7.3.1-5)]
評価プログラム
実行方法
pypy2から
pypy python3_base.py
1229
time:0.756889104843 sec
続いてpypy3です。
pypy3 python3_base.py
1229
time:0.7219762802124023 sec
Julia
インストール
最後にJuliaです。
インストールにつきましてはこちらのHPを参考にさせて頂きました。
はじめに、gfortranをインストールしJuliaのビルド環境を取得します。
sudo apt-get update
sudo apt-get install gfortran
git clone https://github.com/JuliaLang/julia.git
次に、juliaフォルダへ移動しビルドを行います。
makeの実行には10分程度かかりますので気長に待ちましょう。
CPUコア数が1つのためか-j4オプションを付けるとビルドが停止してしまったので外しています。
cd julia
sh contrib/download_cmake.sh
make
最後にPATHを通して終わりです。
echo "export PATH=~/julia:$PATH" >> ~/.bashrc
source .bashrc
下記のバージョン確認ができればインストール完了です。
julia -v
julia version 1.4.1
評価プログラム
Julia言語を用いて同様のプログラムを(見よう見まねで)記載しました。
julia_base.jlというファイル名で保存します。
function prime_jud(num)
ret = 1
for i=2:num-1
if (num%i) == 0
ret = 0
end
end
return ret
end
function prime_count(max_num)
cnt = 0
for i=2:max_num-1
cnt += prime_jud(i)
end
return cnt
end
max_num = 10000
@time ret = prime_count(max_num)
println(ret)
実行方法
下記のように実行します。
julia julia_base.jl
下記実行結果になります。プログラムに自信がなかったのですが、素数の数が1229個と出力されていますので正しく計算できているようです。
0.211689 seconds (20.80 k allocations: 1.254 MiB)
1229
C言語
gccのインストールや実行方法については、みなさんご存じと思いますので省略します。評価プログラムのみ記載します。
評価プログラム
#include <stdio.h>
#include <time.h>
int prime_jud(int num) {
int ret = 1;
for (int i=2; i<num; i++) {
if ((num%i) == 0) {
ret = 0;
}
}
return ret;
}
int prime_count(int max_num) {
int cnt = 0;
for (int i=2; i<max_num; i++) {
cnt += prime_jud(i);
}
return cnt;
}
int main()
{
int ret;
int max_num = 10000;
double start = clock();
ret = prime_count(max_num);
double end = clock();
double time = (end - start) / CLOCKS_PER_SEC;
printf("%d %f\n", ret, time);
return 0;
}
まとめ
Python関連の様々な高速化方法について処理時間を比較しました。やはりコンパイル系は速かったですね。中でも、Cythonは最も高速となりましたので、pythonプログラムをベースに高速化を実現したい場合には有力な選択肢になりそうです。
ただ、Cythonはプログラムの修正が必要であるため、プログラム修正することなくお手軽に高速化を試すにはPyPyが良さそうです。NumbaやPyPyはコード修正が軽微または無しで使えますが、ライブラリの対応や書き方等に制限があるためライブラリを多用する複雑なプログラムに対してどこまで移行できるかについては、よく確認する必要がありそうです。
まずは、お手軽に高速化を試すには、PyPyが最初の一歩になりそうです。
ここまで読んでいただきありがとうございました。
コメント
test