目次
- 目次
- はじめに
- Python JITコンパイラライブラリ Numba
- Numbaのインストール
- Numbaの速度比較
- 引数の指定
- GPUの利用
- 最後に
- 参考資料
- MyEnigma Supporters
はじめに
Pythonは言語的にも、
エコシステム的にも大好きなのですが、
いかんせん、複雑で大規模なシステムを作ると、
速度が遅いのが気になる時があります。
下記の記事のように、
Pythonの一部をC/C++コードに置き換えてもいいのですが、
やっぱりコードを置き換えるのは面倒ですね。。
(またコードを変換している時に、
バグを埋め込んでしまう可能性もあります)
そこで、色々調べた所、
JIT (Just In Time) コンパイラという技術を利用した
Numbaというライブラリを使うことで、
ある特定のPythonコードを修正無しで、
かなり高速化できたので、
その方法を紹介したいと思います。
Python JITコンパイラライブラリ Numba
NumbaはJust In Time コンパイラ (実行時コンパイラ)
という技術を使って、既存のPythonコードを高速化するライブラリです。
Numba: A High Performance Python Compiler
このnumbaはAnacondaを開発している
Continuum Analytics, Incが開発しています。
通常のPythonコードは、スクリプト言語であるため、
毎行毎にコンパイルを実施しながら、
コードを実行します。
そのため、forループのようなコードを書くと、
毎回のコンパイルのオーバヘッドが大きく、
事前コンパイルでループ処理を最適化するC++などに比べて、
かなり処理時間が大きくかかってしまいます。
そのため、Pythonを高速化するためには、
ベクトル化と呼ばれる、行列演算などを駆使することで
高速化するのが一般的です。
(これはMATLABも同じです。
しかし、多少複雑な条件分岐などを使って、
行列を処理したい場合、
上記のベクトル化が難しい場合があります。
そんな時は、このNumbaを使うことで、
forループを使うコード(関数)を事前にコンパイルして、
最適化することで、
C/C++のようなパフォーマンスを得ることができるようです。
NumbaはLLVMという、
最新のコンパイラフレームワークを使って、
指定されたPython関数をコンパイルしてくれるため、
非常に高い性能を得ることができるようです。
Numbaのインストール
anacondaを使ったほうがいいようです。
自分の場合pyenvの環境に、
anacondaの環境を作って、
$ conda install numba
でインストールしました。
Numbaの速度比較
下記のような、
Pythonにおいては非常によくないコードがあるとします。
#! /usr/bin/python # -*- coding: utf-8 -*- from numpy import arange import time def sum2d(arr): M, N = arr.shape result = 0.0 for i in range(M): for j in range(N): result += arr[i, j] return result start = time.time() a = arange(100000000).reshape(10000, 10000) print(sum2d(a)) elapsed_time = time.time() - start print ("elapsed_time:{0}".format(elapsed_time) + "[sec]")
内容としては10000x10000の行列の要素の和を
ループで足し合わせる関数です。
Pythonにとって、forループはご法度なのですが、
無理やり計算しています。
上記のコードを実行すると、
自分の環境の場合、
計算時間に33秒ぐらいかかっています。
一方、下記のように、
numbaのjitモジュールをimportして、
先程のコードに@jitとデコレータを付けるだけで、
下記のsum2d関数がJITで最適化コンパイルされます。
#! /usr/bin/python # -*- coding: utf-8 -*- from numba import jit from numpy import arange import time # jit decorator tells Numba to compile this function. # The argument types will be inferred by Numba when function is called. @jit def sum2d(arr): M, N = arr.shape result = 0.0 for i in range(M): for j in range(N): result += arr[i, j] return result start = time.time() a = arange(100000000).reshape(10000, 10000) print(sum2d(a)) elapsed_time = time.time() - start print ("elapsed_time:{0}".format(elapsed_time) + "[sec]")
上記のコードを実行すると、
同じ環境であっても、
計算時間が0.7秒(!!!)になりました。
モジュールをインポートして、
一行コードを追加するだけで、
40倍以上も高速になりました。
元のコードを全く修正なくていいのが最高ですね。
引数の指定
下記の記事の通り、@jitだけでなく、
引数の型を指定することで、
より高速になるようです。
yutori-datascience.hatenablog.com
GPUの利用
Numbaは通常のCPU処理だけでなく、
GPUの処理にも対応しています。
ただ、下記の記事の通り、
先程は@jitのデコレータを追加するだけでしたが、
GPUを使う場合、GPUのライブラリであるCUDAをつかうために、
かなり処理を変更する必要があるようで、簡単には使えなそうです。
yutori-datascience.hatenablog.com
yutori-datascience.hatenablog.com
yutori-datascience.hatenablog.com
最後に
ほとんどPythonのコードに変更無しに
高速化できるのはすばらしいですね。
ライブラリではなく、
Pythonそのものに入れてもいいのではないかと思いました。
参考資料
yutori-datascience.hatenablog.com
yutori-datascience.hatenablog.com
yutori-datascience.hatenablog.com
MyEnigma Supporters
もしこの記事が参考になり、
ブログをサポートしたいと思われた方は、
こちらからよろしくお願いします。