PyPyとは何ですか?痛みのない高速Python

Pythonは、強力で柔軟性があり、操作が簡単であるという評判を得ています。これらの美徳は、多種多様なアプリケーション、ワークフロー、およびフィールドでの使用につながりました。しかし、言語の設計(その解釈された性質、実行時のダイナミズム)は、PythonがCやC ++などのマシンネイティブ言語よりも常に1桁遅いことを意味します。

何年にもわたって、開発者はPythonの速度制限に対するさまざまな回避策を考え出してきました。たとえば、パフォーマンスを重視するタスクをCで記述し、Pythonでラップすることができます。多くの機械学習ライブラリはまさにこれを行っています。または、Pythonコードに実行時型情報を振りかけてCにコンパイルできるプロジェクトであるCythonを使用することもできます。

ただし、回避策は決して理想的ではありません。既存のPythonプログラムそのまま使用して、劇的に高速に実行できたら素晴らしい と思いませんか?それはまさにPyPyがあなたにできることです。

関連ビデオ:Python用のPyPyランタイムの使用

PyPyとCPython

PyPyは、標準のPythonインタープリターであるCPythonのドロップイン代替品です。CPythonはPythonを中間バイトコードにコンパイルして仮想マシンで解釈しますが、PyPyはジャストインタイム(JIT)コンパイルを使用してPythonコードをマシンネイティブのアセンブリ言語に変換します。

実行するタスクによっては、パフォーマンスが大幅に向上する場合があります。平均して、PyPyはPythonを約7.6倍高速化し、一部のタスクは50倍以上高速化します。CPythonインタープリターは、PyPyと同じ種類の最適化を実行しないだけであり、設計目標の1つではないため、おそらく実行されません。

最良の部分は、PyPyが提供する利益を解き放つために、開発者の側でほとんどまたはまったく努力を必要としないことです。CPythonをPyPyに交換するだけで、ほとんどの場合完了です。以下で説明するいくつかの例外がありますが、PyPyの目標は、既存の変更されていないPythonコードを実行し、自動速度ブーストを提供することです。

PyPyは現在、プロジェクトのさまざまな化身として、Python2とPython3の両方をサポートしています。つまり、実行するPythonのバージョンに応じて、異なるバージョンのPyPyをダウンロードする必要があります。PyPyのPython2ブランチはずっと長く存在していましたが、Python3バージョンは最近スピードアップしました。現在、Python 3.5(本番品質)とPython 3.6(ベータ品質)の両方をサポートしています。

PyPyは、すべてのコアPython言語をサポートすることに加えてpip 、パッケージングや virtualenv 仮想環境など、Pythonエコシステムのツールの大部分と連携し ます。以下で説明する制限はありますが、ほとんどのPythonパッケージは、Cモジュールを備えたものであってもそのまま動作するはずです。

PyPyの仕組み

PyPyは、動的言語用の他のジャストインタイムコンパイラに見られる最適化手法を使用します。実行中のPythonプログラムを分析して、プログラムで作成および使用されるオブジェクトの型情報を判別し、その型情報をガイドとして使用して処理を高速化します。たとえば、Python関数が1つまたは2つの異なるオブジェクトタイプでのみ機能する場合、PyPyはそれらの特定のケースを処理するためのマシンコードを生成します。

PyPyの最適化は実行時に自動的に処理されるため、通常、パフォーマンスを微調整する必要はありません。上級ユーザーは、PyPyのコマンドラインオプションを試して、特別な場合に高速なコードを生成することができますが、これが必要になることはめったにありません。

PyPyは、CPythonが一部の内部関数を処理する方法とも異なりますが、互換性のある動作を維持しようとします。たとえば、PyPyはCPythonとは異なる方法でガベージコレクションを処理します。すべてのオブジェクトがスコープ外になるとすぐに収集されるわけではないため、PyPyで実行されているPythonプログラムは、CPythonで実行されている場合よりも大きなメモリフットプリントを示す可能性があります。しかし、あなたはまだを介して公開Pythonの高レベルのガベージコレクションのコントロールに使用できるgcようなモジュールを、gc.enable()gc.disable()、とgc.collect()

実行時のPyPyのJIT動作に関する情報が必要な場合、PyPyには、pypyjitPythonアプリケーションに多くのJITフックを公開するモジュールが含まれています。JITでパフォーマンスが低下していると思われる関数またはモジュールがある場合は、pypyjitそれに関する詳細な統計を取得できます。

別のPyPy固有のモジュールであるは__pypy__、PyPyに固有の他の機能を公開しているため、これらの機能を活用するアプリの作成に役立ちます。Pythonのランタイムダイナミズムにより、PyPyが存在する場合はこれらの機能を使用し、存在しない場合は無視するPythonアプリを構築することができます。

PyPyの制限

PyPyのように魔法のように見えるかもしれませんが、それは魔法ではありません。PyPyには、特定の種類のプログラムに対するその有効性を低下または回避する特定の制限があります。残念ながら、PyPyはストックCPythonランタイムの完全に普遍的な代替品ではありません。

PyPyは純粋なPythonアプリで最適に動作します

PyPyは、常に「純粋な」Pythonアプリケーション、つまりPythonで記述されたアプリケーションで最高のパフォーマンスを発揮します。PyPyがCPythonのネイティブバイナリインターフェイスをエミュレートする方法のため、NumPyなどのCライブラリとインターフェイスするPythonパッケージもうまくいきませんでした。 

PyPyの開発者はこの問題に気を配り、C拡張機能に依存するPythonパッケージの大部分とPyPyの互換性を高めました。たとえば、NumpyはPyPyで非常にうまく機能するようになりました。ただし、C拡張機能との互換性を最大限に高めたい場合は、CPythonを使用してください。

PyPyは、実行時間の長いプログラムで最適に動作します

PyPyがPythonプログラムを最適化する方法の副作用の1つは、実行時間の長いプログラムがその最適化から最も恩恵を受けることです。プログラムの実行時間が長いほど、PyPyが収集できる実行時型情報が多くなり、最適化が可能になります。1回限りのPythonスクリプトは、この種のメリットを享受できません。メリットのあるアプリケーションには、通常、長期間実行されるループ、またはバックグラウンドで継続的に実行されるループ(Webフレームワークなど)があります。

PyPyは事前コンパイルを行いません

PyPyは  Pythonコードをコンパイルしますが、Pythonコードのコンパイラではありません  。PyPyが最適化を実行する方法とPythonに固有のダイナミズムのため、結果のJITtedコードをスタンドアロンバイナリとして出力して再利用する方法はありません。各プログラムは、実行ごとにコンパイルする必要があります。Pythonをスタンドアロンアプリとして実行できるより高速なコードにコンパイルする場合は、Cython、Numba、または現在実験中のNuitkaプロジェクトを使用してください。