PyPyを使い始める

0 株式
0
0
0
0

導入

Pythonプログラミング言語は、様々な方法で実装できるインターフェースです。例としては、C言語を使用するCPython、Javaを使用して実装されるJythonなどが挙げられます。.

CPythonは最も人気があるにもかかわらず、最速ではありません。PyPyは互換性と高速性を兼ね備えたPythonの代替実装です。PyPyはJITコンパイルを採用しており、長時間実行される処理の実行時間を大幅に短縮します。.

このチュートリアルでは、初心者向けにPyPyを紹介し、CPythonとの違いを詳しく説明します。また、PyPyのメリットと制限についても解説します。さらに、PyPyをダウンロードして使い、簡単なPythonスクリプトを実行する方法も解説します。.

具体的には、このトレーニングでは次の内容をカバーします。

  • CPythonの簡単な概要
  • PyPyとその機能の紹介
  • PyPyの制限
  • UbuntuでPyPyを実行する
  • PyPyとCPythonランタイムの比較

CPythonの簡単な概要

PyPyについて議論する前に、CPythonがどのように動作するかを理解することが重要です。以下は、CPythonを使用して実装されたPythonスクリプトの実行パイプラインの図です。.

Pythonスクリプト .py が与えられると、まずCPythonコンパイラを用いてソースコードをバイトコードにコンパイルします。生成されたバイトコードは拡張子pycのファイルに保存されます。その後、バイトコードはCPythonインタープリタを用いて仮想環境で実行されます。.

コンパイラを使用してソースコードをバイトコードに変換することには利点があります。コンパイラを使用しない場合、インタープリタはソースコードに直接取り組み、行ごとにマシンコードに変換します。これを行う欠点は、ソースコードの各行をマシンコードに変換するためにいくつかのプロセスを適用する必要があり、そのようなプロセスが各行に対して繰り返されることです。たとえば、構文解析は各行に対して他の行とは独立して適用されるため、インタープリタはコードの変換に多くの時間を費やします。コンパイラはすべてのコードを一度に処理できるため、構文解析はコード行ごとにではなく 1 回だけ適用されるため、この問題を解決します。そのため、コンパイラから生成されるバイトコードは簡単に解釈できます。ソースコード全体をコンパイルすることが場合によっては役に立たない場合があることに注意してください。PyPy について説明するときに、この明確な例を見ることになります。.

バイトコードが生成されると、仮想マシン上で動作するインタープリタによって実行されます。仮想環境はCPythonバイトコードをマシンから分離し、Pythonをクロスプラットフォーム化するという利点があります。.

残念ながら、コンパイラを使ってバイトコードを生成するだけでは、CPythonの実行速度を向上させるのに十分ではありません。インタプリタは、実行されるたびにコードを機械語に変換することで動作します。つまり、ある行の実行にLX秒かかる場合、それを10回実行するとX*10秒かかります。長時間実行される処理の場合、これは実行時間に大きな負担となります。.

CPython のバグに基づいて、今度は PyPy を見てみましょう。.

PyPyとその機能の紹介

PyPyはCPythonに似たPython実装で、準拠と高速性を兼ね備えています。「準拠」とは、PyPyがCPythonと互換性があるという意味で、PyPyではほぼすべてのCPythonコマンドを使用できることを意味します。ここで述べたように、互換性にはいくつかの違いがあります。PyPyの最大の利点は、その速度です。PyPyはCPythonよりもはるかに高速です。後ほど、PyPyが約7倍高速であることを示すテストをいくつか紹介します。場合によっては、CPythonの数十倍、数百倍も高速になることもあります。では、PyPyはどのようにしてその速度を実現しているのでしょうか?

スピード

PyPyは、Pythonスクリプトを劇的に高速化できるジャストインタイム(JIT)コンパイラを使用します。CPythonで使用されるコンパイル方式はAhead-of-Time(AOT)で、すべてのコードは実行前にバイトコードに変換されます。JITは実行時に必要な場合にのみコードを変換します。.

ソースコードには、実際には実行されないコードブロックが含まれている場合がありますが、AOTコンパイラによって変換されます。その結果、処理時間が遅くなります。ソースコードが大規模で数千行に及ぶ場合、JITの使用は大きな効果を発揮します。AOTではソースコード全体が変換されるため、多くの時間がかかります。JITでは、必要なコード部分のみが実行されるため、処理速度が大幅に向上します。.

PyPyはコードを翻訳した後、キャッシュします。つまり、コードは一度だけ翻訳され、後でその翻訳結果が使用されるということです。CPythonインタープリタはコードが実行されるたびに翻訳を繰り返すため、これも速度低下の要因の一つとなっています。.

楽々と

PyPyはPythonスクリプトのパフォーマンスを向上させる唯一の方法ではありませんが、最も簡単な方法です。例えば、Cythonを使用すると、C言語の型を変数に代入する処理を高速化できます。問題は、Cythonでは開発者がソースコードを手作業でレビューして最適化する必要があることです。これは面倒な作業であり、コードサイズが大きくなるにつれて複雑さが増します。PyPyを使用すれば、通常のPythonコードを手間をかけずにはるかに高速に実行できます。.

スタックなし

標準のPythonはCスタックを使用します。このスタックは、互いに呼び出される(戻り値を返す)関数のシーケンスを格納します。スタックのサイズには制限があるため、関数呼び出しの回数にも制限があります。.

PyPyは、C言語のスタックを使用しないPython実装であるStackless Pythonを使用します。代わりに、関数呼び出しはオブジェクトと並んでスタック上に保存されます。スタックサイズはヒープサイズよりも大きいため、より多くの関数呼び出しが可能です。.

スタックレスPythonは、通常のPythonスレッドよりも優れたマイクロスレッドもサポートしています。スタックレスPythonスレッドでは、「タスクレット」と呼ばれる数千ものタスクをすべて単一のスレッドで実行できます。.

タスクレットを使用すると、タスクを並行して実行できます。並行性とは、2つのタスクが同時に実行され、同じリソースを共有することを意味します。1つのタスクはしばらく実行された後、停止して2つ目のタスクの実行スペースを確保します。これは、2つの別々のタスクを同時に実行する並列処理とは異なることに注意してください。.

タスクレットを使用すると、作成されるスレッド数が削減され、オペレーティングシステムによるすべてのスレッド管理のオーバーヘッドが削減されます。その結果、2つのスレッド間の切り替えによる実行速度向上は、2つのタスク間の切り替えよりも時間がかかります。.

Stackless Pythonの使用により、継続処理も可能になりました。継続処理を使用すると、タスクの状態を保存し、後で復元してタスクを続行できます。Stackless Pythonは標準のPythonと何ら変わりありません。単に機能が追加されているだけです。標準のPythonで利用できるものはすべて、Stackless Pythonでも利用できます。.

PyPy の利点について説明した後、次のセクションではその制限について説明します。.

PyPyの制限

CPython はどのマシンでもどの CPU アーキテクチャでも使用できますが、PyPy のサポートは比較的限られています。.

PyPy によってサポートおよび保守されている CPU アーキテクチャは次のとおりです (ソース):

  • x86 (IA-32) と x86_64
  • ARM プラットフォーム (ARMv6 または ARMv7、VFPv3 搭載)
  • AArch64
  • PowerPC 64ビット、リトルエンディアンとビッグエンディアンの両方
  • システムZ(s390x)

PyPyはすべてのLinuxディストリビューションで動作するわけではないため、サポートされているディストリビューションを使用するように注意してください。サポートされていないディストリビューションでPyPy Linuxバイナリを実行するとエラーが返されます。PyPyはPython 2とPython 3の1つのバージョン、つまりPyPy 2.7とPyPy 3.6のみをサポートしています。.

PyPyで実行されるコードが純粋なPythonである場合、PyPyによる高速化は通常顕著です。しかし、コードにNumPyなどのC拡張が含まれている場合、PyPyによって実行時間が実際に長くなる可能性があります。PyPyプロジェクトは活発に開発されているため、将来的にはC拡張のサポートが強化される可能性があります。.

PyPyは、Kivyなどの多くの一般的なPythonフレームワークではサポートされていません。KivyはAndroidやiOSを含むすべてのプラットフォームでCPythonを実行できます。つまり、PyPyはモバイルデバイスでは実行できません。.

PyPy の利点と制限を確認したので、Ubuntu で PyPy を実行する方法を説明しましょう。.

UbuntuでPyPyを実行する

PyPyはMac、Linux、Windowsで実行できますが、ここではUbuntuでの実行について説明します。PyPyのLinuxバイナリは特定のLinuxディストリビューションでのみサポートされていることに注意してください。利用可能なPyPyバイナリとサポートされているディストリビューションについては、こちらのページで確認できます。例えば、PyPy(またはPython 2.7またはPython 3.6)は、Ubuntuの18.04、16.04、14.04の3つのバージョンでのみサポートされています。現時点で最新バージョンのUbuntu(19.10)を使用している場合、PyPyを実行することはできません。サポートされていないディストリビューションでPyPyを実行しようとすると、次のエラーが返されます。

PyPyバイナリは圧縮ファイルとして提供されます。ダウンロードしたファイルを解凍するだけです。解凍されたフォルダの中にbinというフォルダがあり、そこにPyPy実行ファイルがあります。私はPython 3.6を使用しているので、ファイル名はpypy3です。Python 2.7の場合は、単にpypyです。.

CPythonの場合、ターミナルからPython 3を実行するには、コマンド python3 を入力するだけです。PyPyを実行するには、コマンド pypy3 を実行するだけです。.

下図に示すように、ターミナルでpypy3コマンドを入力すると、「pypy3が見つかりません」というメッセージが表示される場合があります。これは、PyPyのパスがPATH環境変数に追加されていないことが原因です。ターミナルの現在のパスがPyPyのbinディレクトリ内にある場合、実際に機能するコマンドは./pypy3です。ドット「.」は現在のディレクトリを指し、「/」は現在のディレクトリ内のファイルにアクセスするために追加されます。./pypy3コマンドを実行すると、Pythonが正常に実行されます。.

これで、PyPyを活用して、いつも通りPythonを操作できるようになりました。例えば、1000個の数値を加算する簡単なPythonスクリプトを作成し、PyPyを使って実行してみましょう。コードは以下のとおりです。.

nums = range(1000)
sum = 0
for k in nums:
sum = sum + k
print("Sum of 1,000 numbers is : ", sum)

このスクリプトの名前が test.py の場合、次のコマンドを使用して簡単に実行できます (Python ファイルが PyPy bin フォルダー内にあると仮定します。これは pypy3 コマンドと同じ場所です)。.

./pypy3 test.py

次の図は、前のコードを実行した結果を示しています。.

PyPyとCPythonランタイムの比較

1000 個の数値の合計に対する PyPy と CPython の実行時間を比較するには、時間を測定するコードを次のように変更します。.

import time
t1 = time.time()
nums = range(1000)
sum = 0
for k in nums:
sum = sum + k
print("Sum of 1,000 numbers is : ", sum)
t2 = time.time()
t = t2 - t1
print("Elapsed time is : ", t, " seconds")

PyPyの場合、実行時間は0.00045秒近くになりますが、CPythonの場合は0.0002秒です(このコードはCore i7-6500Uマシン(2.5GHz)で実行しました)。この場合、CPythonはPyPyよりも時間が短くなっていますが、これはそれほど長時間実行されないタスクなので当然の結果です。もしコードが1,000ではなく100万の数値を加算するのであれば、最終的にはPyPyが勝利するでしょう。この場合、PyPyでは0.00035秒、CPythonでは0.1秒かかります。これでPyPyの優位性が明らかになりました。これにより、長時間実行タスクにおいてCPythonがどれだけ遅いかがお分かりいただけると思います。.

結果

このチュートリアルでは、最速のPython実装であるPyPyを紹介します。PyPyの主な利点は、コンパイル済みのマシンコードをキャッシュして再実行を防ぐジャストインタイム(JIT)コンパイルです。PyPyの限界についても説明します。主な限界は、純粋なPythonコードには適していますが、C拡張には適していないことです。.

また、PyPyがUbuntu上でどのように動作するかを確認し、CPythonとPyPyの実行時間を比較しました。その結果、長時間実行タスクではPyPyのパフォーマンスが優れていることが分かりました。一方、短時間実行タスクではCPythonがPyPyに勝る可能性も残っています。今後の記事で、PyPy、CPython、Cythonの比較をさらに詳しく取り上げます。.

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

あなたも気に入るかもしれない