Python は強力で柔軟、かつ簡単に扱えるという評判を得ています。 これらの美徳により、膨大で増え続けるさまざまなアプリケーション、ワークフロー、および分野で使用されるようになりました。 しかし、言語の設計、すなわち解釈される性質、実行時のダイナミズム、は Python が C や C++ のようなマシン ネイティブ言語よりも常に桁違いに遅いことを意味しています。 たとえば、C 言語でパフォーマンス重視のタスクを記述し、それを Python でラップすることができます。 あるいは、Cython を使用することもできます。Cython は、Python コードを C にコンパイルできるようにする実行時型情報を散りばめることができるプロジェクトですが、
回避策は決して理想的なものではありません。 既存の Python プログラムをそのまま利用し、劇的に速く実行できたら素晴らしいと思いませんか? 8182>
PyPy vs. CPython
PyPy は、標準の Python インタプリタである CPython のドロップイン置き換え製品です。 CPython が Python を中間バイトコードにコンパイルし、それを仮想マシンで解釈するのに対し、PyPy ではジャスト イン タイム (JIT) コンパイルを使用して Python コードをマシン ネイティブのアセンブリ言語に変換します。 平均して、PyPy は Python を約 7.6 倍高速化し、一部のタスクでは 50 倍以上高速化します。 CPython インタープリターは単に PyPy と同じ種類の最適化を実行しませんし、それは設計目標の 1 つではないので、おそらく今後も実行しないでしょう。 単に CPython を PyPy に交換するだけで、ほとんどの場合、完了します。 後述するいくつかの例外はありますが、PyPy の明示的な目標は、既存の、変更されていない Python コードを実行し、それを自動的に高速化することを提供することです。 言い換えれば、実行する Python のバージョンに応じて、異なるバージョンの PyPy をダウンロードする必要があります。 PyPyのPython 2ブランチはより長く存在していますが、Python 3バージョンは最近になってスピードアップしています。 現在、Python 3.5 (製品版) と Python 3.6 (ベータ版) の両方をサポートしています。
コア Python 言語をすべてサポートしていることに加え、PyPy はパッケージング用の pip
や仮想環境用の virtualenv
などの Python エコシステムの大半のツールと共に動きます。
How PyPy works
PyPy は動的言語用の他のジャストインタイムコンパイラーで見られる最適化技術を使用しています。 実行中のPythonプログラムを解析し、プログラム内で生成・使用されるオブジェクトの型情報を判断し、その型情報を指針として高速化を図ります。 たとえば、Python の関数が 1 つか 2 つの異なるオブジェクト型だけで動作する場合、PyPy はそれらの特定のケースを処理する機械コードを生成します。
PyPy の最適化は実行時に自動的に処理されるので、通常、そのパフォーマンスを調整する必要はありません。 上級ユーザーは特殊なケースでより速いコードを生成するために PyPy のコマンドラインオプションを試すかもしれませんが、これが必要になるのはまれです。
PyPyもCPythonがいくつかの内部機能を扱う方法から外れていますが、互換性を保つように努めています。 たとえば、PyPy はガベージコレクションを CPython とは異なる方法で扱います。 そのため、PyPy で動作する Python プログラムは、CPython で動作するときよりも大きなメモリフットプリントを示すかもしれません。 しかし、gc
モジュールを通して公開されている、gc.enable()
, gc.disable()
, gc.collect()
といったPythonの高レベルガベージコレクションコントロールを使うことはできます。
実行時にPyPyのJIT動作についての情報が欲しい場合、PyPyにはpypyjit
モジュールというPythonアプリケーションに多くのJITフックを公開するものが用意されています。 もし JIT のパフォーマンスが悪いと思われる関数やモジュールがあれば、pypyjit
を使ってその詳細な統計情報を得ることができます。
別の PyPy 固有のモジュール __pypy__
は PyPy に固有の他の機能を公開しており、それらの機能を利用したアプリケーションを書くのに便利です。 Python のランタイムダイナミズムにより、PyPy が存在するときはこれらの機能を使用し、存在しないときは無視する Python アプリケーションを構築することが可能です。 PyPy には、ある種のプログラムに対するその有効性を減少させる、または回避する、ある種の制限があります。 残念ながら、PyPy は純正の CPython ランタイムの完全に普遍的な置き換えではありません。
PyPy works best with pure Python apps
PyPy は常に “pure” Python アプリケーション、すなわち Python で書かれそれ以外何もないアプリケーションで最高のパフォーマンスを発揮してきました。 NumPy のような C ライブラリとのインタフェースを持つ Python パッケージは、PyPy が CPython のネイティブバイナリインタフェースをエミュレートする方法のため、あまりうまく行きません。
PyPy の開発者はこの問題を取り除き、C 拡張に依存する Python パッケージの大部分と PyPy との互換性を高めました。 たとえば、Numpy は現在 PyPy で非常にうまく動作します。 しかし、C 拡張との互換性を最大にしたい場合は、CPython を使用してください。
PyPy works best with longer-running programs
PyPy が Python プログラムを最適化する方法の副作用として、長く実行するプログラムはその最適化から最も恩恵を受けるということが挙げられます。 プログラムの実行時間が長いほど、PyPyが収集できる実行時の型情報は多くなり、より多くの最適化を行うことができます。 一回きりのPythonスクリプトはこのような恩恵にあずかることはありません。
PyPy does not do ahead-of-time compilation
PyPy は Python コードをコンパイルしますが、それは Python コードのためのコンパイラではありません。 PyPy が最適化を行う方法と Python 固有のダイナミズムのために、JIT された結果のコードをスタンドアロンバイナリとして出力して再利用する方法がありません。 各プログラムは、実行のたびにコンパイルする必要があります。 もし Python をスタンドアロンアプリケーションとして実行できるより高速なコードにコンパイルしたければ、 Cython や Numba、あるいは現在実験中の Nuitka プロジェクトを使ってください
。