NumbaのJITでPythonを高速化したら40倍も速くなった

  • このエントリーをはてなブックマークに追加

Pythonは人気のある言語ですが処理速度は速くありません。しかし、NumbaのJITコンパイルを使う事で簡単に高速化が可能です。ここではNumbaのインストールからベンチマークテストを行い、最大40倍の高速化に成功した事例を紹介します。

こんにちは。wat(@watlablog)です。ここでは、処理速度がネックのPythonをNumbaというライブラリで高速化する方法を紹介します

Pythonが遅い理由

Pythonを高速化する前に、なぜPythonはこんなにも遅いのかを調べてみました。そういえばPython関係のブログを始めて1年ほど経ちますが、処理速度については全く意識していませんでした。

Pythonは特にfor文といったループ系の文が遅い遅いと世間で言われています。

でもなんで遅いのでしょう?
正直僕はそんなに情報工学に詳しくないので、中々イメージできていませんでしたが、以下に理解の参考となったブログがありましたのでいくつかメモしておきます。

“GIL(グローバルインタプリタロック)であるため”
“インタプリタ言語で、コンパイルされないため”
“動的型付き言語であるため”

POSTD:なぜPythonはこんなにも遅いのか?

…Pythonが遅い理由には色々な要因が考えられるみたいですが、動的型付き言語であるというのが一番あやしいようです。
for文では逐一型チェックをしているようです。

Pythonではfor文を書いたら負け」という言葉も良く聞きます。できるだけnumpyの行列演算で済ますというのが定石です。

しかし、漸化式を解く等、for文を使わないと厳しい場面もあります。

今回はfor文を書いてもそんなに負けない方法としてNumbaを使ってみます。

Numbaを使ってPythonコードを高速化する方法

初心者のPythonコード高速化にNumbaを勧める理由

PythonはかのNumpyや機械学習関係を始めとした特殊な外部ライブラリが豊富です。そのため、他の型宣言を必要とした高速な言語を中々使いたくない人が沢山いらっしゃいます。

そして、Pythonコードの一部が遅いという欠点を補うライブラリも様々あることがPythonの人気の1つとも言えます。

高速化の代表格にCythonというものがあります。CythonはコードをC/C++に変換することによって高速化するものですが、Pythonicな書き方といってもC/C++の知識は必須であったり、割と「手軽にはやくしたい!」という初心者にはハードルが高かったりします。

今回紹介するNumbaJITコンパイルという手法を用いた高速化を行うライブラリです。

JITとは、Just In Timeの略で、JITコンパイルはプログラムの実行時に、予め中間コードにコンパイルしてから実行することを意味します。

そしてNumbaはおそろしく手軽に高速化を試すことができます。それでは、早速Numbaのインストールを行って実際に高速化を体験してみたいと思います。

Numbaのインストール

Numbaは以下のコードでpipインストールが可能です。

インストールはすぐに終わるので、以下の検証用コードで試してみましょう。

関数の前に「@jit」と書くだけでPythonコードの高速化を試すことができます。

フィボナッチ数列で検証

for文の速度変化を試すのにうってつけの計算の1つに、フィボナッチ数列を求める漸化式があります。
この数列は,

\[0, 1, 1, 2, 3, 5, 8, 13, 21, \cdots\]

...となり、\(f_{n}=f_{n-1}+f_{n-2}\)で計算できます。

素晴らしい一般項はあるのですが、あえて漸化式をfor文で解きます。Pythonでfor文を書いて負けてみます。

JIT無しの速度

フィボナッチ数のwikipediaページにも載っているPythonコードは以下です。

計算時間をimportしたtimeで計測しながら、無駄に100万項求めます。

CPUは1.6GHz、RAMは8GBの弊PCで上記コードを実行すると、以下の結果を得ます。26[s]以上もかかっていますね。

JIT有りの速度

それでは、次はJITを使ってみましょう。使い方は簡単。numbaからjitをimportし、def関数の前に@jitを付けているだけです。

計測結果はなんと0.6[s]ほどになりました。先ほどの26[s]と比べると43.6倍の高速化がなされたこととなります!

マンデルブロ集合で検証

フィボナッチ数列だけでは月並みなので、マンデルブロ集合の計算でも試してみます。マンデルブロ集合については「Pythonで描くマンデルブロ集合!フラクタルの旅を体感してみる」の記事で紹介していますので、ご興味がありましたら是非読んでみて下さい。

以下にマンデルブロ集合を描画するコードを再掲します。ここではJIT有りの場合のみを記載します。

JIT無しの速度

まずはJIT無しの結果です。漸化式をforループで解くのに289[s]もかかっています。

JIT有りの速度

次に、JIT有りの結果です。なんと289[s]がわずか7[s]ほどになりました!41倍の高速化です!

ちなみに、マンデルブロ集合を計算すると、以下のように美麗な画像を得ることができます。条件をいくつも変更しながら描画を試す時に長時間待ちたくないので、JITによる高速化は大変ありがたいですね。

マンデルブロ集合

両方とも40倍程度の高速化が実現されました!40という数字は共通でしたが、何か理由があるのかな?

まとめ

本記事ではPythonのネックとなっているループ処理時間を高速化する方法として、NumbaというライブラリのJITを紹介しました。

forループでかかれたフィボナッチ数列とマンデルブロ集合を得る漸化式に対してJITを使うことで、共に約40倍の高速化を行うことができました。

Pythonでコードを書くにあたり、NumbaのJITを使った高速化手法は簡単なのに強力な武器になりますね!
Twitterでも関連情報をつぶやいているので、wat(@watlablog)のフォローお待ちしています!

  • このエントリーをはてなブックマークに追加

SNSでもご購読できます。

コメント

  1. 濵田康平 より:

    z0が無限ではなくて2を超えた時にしたほうが計算がはやくなりますよ

    1. wat より:

      アドバイスありがとうございます!
      高速化のテクニックとして元の記事に一言追記しておきました。
      https://watlab-blog.com/2020/05/23/mandelbrot-set/

wat へ返信する コメントをキャンセル

*