Shed Skin-PythonからC ++への実験的な翻訳者

はじめに



Shed Skinは、PythonからC ++への実験的なコンパイラーであり、Python集中プログラムの速度を上げるように設計されています。 Python言語の限られたサブセットで書かれたプログラムをC ++に変換します。 C ++コードは実行可能コードにコンパイルできます。実行可能コードは、別個のプログラムまたは拡張モジュールのいずれかであり、簡単にインポートして通常のPythonプログラムで使用できます。



Shed Skinは、Pythonプログラムで使用されているタイプマッチングメソッドを使用して、C ++バージョンに必要な明示的なタイプ宣言を生成します。 C ++は静的型付き言語であるため、Shed Skinでは、すべての変数が特定の型を持つようにPythonコードを記述する必要があります。

タイピングと言語のサブセットの制限に加えて、サポートされているプログラムは標準のPythonライブラリを自由に使用できませんが、randomやreなどの一般的に使用される約25のモジュールがサポートされています(ライブラリの制限を参照)。

さらに、Shed Skinが使用するタイプ検出テクノロジーは、現在、数千行のコードを超えるプログラムにはあまりうまくスケールしません(ブロードキャストプログラムの最大サイズは約6,000行(sloccount)です)。 一般的に、これは、Shed Skinが現在、Pythonまたは標準および外部ライブラリの動的型付け機能を集中的に使用しない小さなプログラムおよび拡張モジュールの翻訳により適していることを意味します。 以下の75の重要なサンプルプログラムのコレクションを参照してください。

Shed Skinはまだ開発の初期段階にあるため、劇的に改善できます。 現時点では、使用中にエラーが発生する可能性があります。 修正できるように、それらに関するレポートをお送りください!

Shed Skinは現在、Pythonバージョン2.4から2.7と互換性があり、2.6のように動作し、WindowsおよびGNU / LinuxやOSXなどのほとんどのUNIXプラットフォームで動作します。

入力制限



Shed Skinは、通常の静的に型付けされたプログラムをC ++で変換します。 静的型付けの制限は、変数が持つことができる不変型は1つだけであることを意味します。 したがって、たとえば、コード

a = 1 a = '1' #  


受け入れられない。 ただし、C ++の場合と同様に、型はコードなどの抽象型にすることができます

 a = A() a = B() #  


たとえば、AとBには共通の基本クラスがあります。

型付けの制限は、同じコレクション(リスト、セットなど)の要素が異なる型を持つことができないことも意味します(メンバーの型も静的でなければならないため)。 だからコード:

 a = ['apple', 'b', 'c'] #  b = (1, 2, 3) #  c = [[10.3, -2.0], [1.5, 2.3], []] #  


言いましょうが、コード

 d = [1, 2.5, 'abc'] #  e = [3, [1, 2]] #  f = (0, 'abc', [1, 2, 3]) #  


受け入れられない。 辞書のキーと値にはさまざまなタイプがあります。

 g = {'a': 1, 'b': 2, 'c': 3} #  h = {'a': 1, 'b': 'hello', 'c': [1, 2, 3]} #  


Shed Skinの現在のバージョンでは、長さ2のタプルで混合型も使用できます。

 a = (1, [1]) #  


将来的には、より長いタプルで混合型が許可される可能性があります。

None型は、非スカラー型とのみ混合できます(つまり、int、float、boolまたはcomplexとは混合できません)。

 l = [1] l = None #  


 m = 1 m = None #  


 def fun(x = None): # :     x, , x = -1 pass fun(1) 


通常、整数と浮動小数点数(整数と浮動小数点数)は混在できます(整数は浮動小数点数になります)。 これが不可能な場合、Shed Skinはエラーメッセージを表示します。

Pythonサブセットの制限



Shed Skinは常にPython言語のすべての機能のサブセットのみをサポートします。 現在、次の機能はサポートされていません。



他のいくつかの機能は部分的にしかサポートされていません:



ライブラリの制限



現時点では、次の25のモジュールが大幅にサポートされています。 os.pathなど、それらの一部はShed Skinを使用してC ++に変換されています。



pygame、pyqt、pickleなどの他のモジュールは、Shed Skinを使用して生成された拡張モジュールと組み合わせて使用​​できることに注意してください。 そのような使用の例については、「Shed Skin Examples」を参照してください。

設置



インストーラには、ブートアンパックWindowsインストーラとUNIXアーカイブの2種類があります。 しかし、もちろん、Shed Skinがパッケージマネージャーを使用してGNU / Linuxインストールパッケージマネージャー(Shed Skinは少なくともDebian、Ubuntu、Fedora、およびArchで利用可能)をインストールした方が良いです。



Windowsバージョンをインストールするには、インストーラーをダウンロードして実行するだけです。 ActivePythonまたは別のカスタムPythonディストリビューション、またはMingWを使用している場合は、最初にアンインストールします。 また、おそらく64ビットバージョンのPythonでは一部のファイルが欠落しているため、拡張モジュールのアセンブリはできないことに注意してください。 64ビットの代わりに、32ビットバージョンのPythonを使用します。

Unix


パッケージマネージャーを使用したインストール


Ubuntuのコマンド例:

 sudo apt-get install shedskin 


手動インストール


UNIXアーカイブから配布パッケージを手動でインストールするには、次の手順を実行します。



依存関係


shedskinで生成されたプログラムをコンパイルして実行するには、次のライブラリが必要です。



これらのライブラリをUbuntuにインストールするには、次を入力します。

 sudo apt-get install g++ libpcre++dev python-all-dev libgc-dev 


Boehmガベージコレクターがパッケージマネージャーから使用できない場合は、このメソッドを使用します。 たとえば、Webサイトからバージョン7.2alpha6をダウンロードし、解凍して、次のようにインストールします。

 ./configure --prefix=/usr/local --enable-threads=posix --enable-cplusplus --enable-thread-local-alloc --enable-large-config make make check sudo make install 


パッケージマネージャーからPCREライブラリを使用できない場合は、次の方法を使用します。 たとえば、Webサイトからバージョン8.12をダウンロードし、解凍して次のようにインストールします。

 ./configure --prefix=/usr/local make sudo make install 


OSX
手動インストール


OSXシステムのUNIXアーカイブからShed Skinをインストールするには、次の手順を実行します。



依存関係


shedskinによって生成されたプログラムをコンパイルして実行するには、次のライブラリが必要です。



Boehmガベージコレクターがパッケージマネージャーから使用できない場合は、このメソッドを使用します。 たとえば、Webサイトからバージョン7.2alpha6をダウンロードし、解凍して、次のようにインストールします。

 ./configure --prefix=/usr/local --enable-threads=posix --enable-cplusplus --enable-thread-local-alloc --enable-large-config make make check sudo make install 


パッケージマネージャーからPCREライブラリを使用できない場合は、次の方法を使用します。 たとえば、Webサイトからバージョン8.12をダウンロードし、解凍して次のようにインストールします。

 ./configure --prefix=/usr/local make sudo make install 


通常番組の放送



Windowsでは、まずShed Skinをインストールしたディレクトリのinit.batファイルをダブルクリックします。

test.pyという次の簡単なテストプログラムをコンパイルするには:
print 'hello, world!'


入力してください:
shedskin test


Makefileと同様に、test.cppおよびtest.hppという名前の2つのC ++ファイルが作成されます。

test(またはtest.exe)という名前の実行可能ファイルを作成するには、次のように入力します。

 make 


拡張モジュールの作成



次のsimple_module.pyという名前のプログラムを拡張モジュールとしてコンパイルするには:

 # simple_module.py def func1(x): return x+1 def func2(n): d = dict([(i, i*i) for i in range(n)]) return d if __name__ == '__main__': print func1(5) print func2(10) 


入力してください:

 shedskin -e simple_module make 


'make'コマンドをWindows以外のシステムで正常に実行するには、Pythonデバッグファイルがインストールされていることを確認してください(Debianにpython-devをインストールし、Fedoraにpython-develをインストールします)。

型定義を可能にするには、モジュールが独自の関数のみを呼び出す必要があることに注意してください。 この効果は、呼び出しがif __name __ == '__ main__'条件内に配置され、モジュールがインポートされた場合に呼び出されないという事実により、この例で実現されます。 関数は間接的にのみ呼び出すことができます。つまり、func2がfunc1を呼び出す場合、func1の呼び出しは省略できます。

拡張モジュールを簡単にインポートして、通常どおり使用できるようになりました。

 >>> from simple_module import func1, func2 >>> func1(5) 6 >>> func2(10) {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81} 


制限事項


コンパイルされた拡張モジュールと元の拡張モジュールの使用には大きな違いがあります。

組み込みのスカラー型とコンテナ(int、float、complex、bool、str、list、tuple、dict、set)、およびNoneとユーザー定義クラスのインスタンスのみを渡すことができます。 たとえば、匿名関数とイテレータは現在サポートされていません。
インラインオブジェクトとそのコンテンツは、Shed SkinタイプからCPythonへ、またはその逆に、すべての関数呼び出し/戻りに完全に変換されます。 つまり、Shed Skin側の組み込みCPythonオブジェクトを変更することはできず、その逆もできません。また、変換が遅くなる可能性があります。ユーザーが定義したクラスのインスタンスは、変換なしで転送/返され、どの側でも変更できます。
グローバル変数は、初期化中にShed SkinからCPythonに一度だけ変換されます。 つまり、CPythonバージョンとShed Skinバージョンは独立して変更できます。 この問題は、定数のgabola変数のみを使用するか、getterおよびsetter関数を追加することで回避できます。
複数の(相互作用する)拡張モジュールは現在サポートされていません。 また、Pythonバージョンと翻訳バージョンの両方を同時にインポートして使用することはできません。

Numpyとの統合


Shed Skinは現在、Numpyを直接サポートしていません。 ただし、toumpメソッドを使用して、Numpy配列をリストとして翻訳済みShed Skin拡張モジュールに渡すことは可能です。 これは非常に非効率的であることに注意してください(上記参照)。したがって、拡張モジュール内で長時間を費やす場合に使用できます。 次の例を考えてみましょう。

 # simple_module2.py def my_sum(a): """ compute sum of elements in list of lists (matrix) """ h = len(a) # number of rows in matrix w = len(a[0]) # number of columns s = 0.0 for i in range(h): for j in range(w): s += a[i][j] return s if __name__ == '__main__': print my_sum([[1.0, 2.0], [3.0, 4.0]]) 


Shed Skinを使用してこのモジュールを拡張モジュールとして翻訳した後、次のようにNumpy配列を渡すことができます。

 >>> import numpy >>> import simple_module2 >>> a = numpy.array(([1.0, 2.0], [3.0, 4.0])) >>> simple_module2.my_sum(a.tolist()) 10.0 


バイナリ分布



生成されたWindowsバイナリコードを別のシステムで使用するか、init.batを実行せずに実行するには、バイナリファイルがあるディレクトリに次のファイルを配置します。

shedskin-0.9 \ shedskin \ gc.dll
shedskin-0.9 \ shedskin-libpcre-0.dll
shedskin-0.9 \ bin \ libgcc_s_dw-1.dll
shedskin-0.9 \ bin \ libstdc ++。dll

Unix


生成されたバイナリを別のシステムで使用するには、libgcとlibpcre3がそこにインストールされていることを確認してください。 これが当てはまらず、システムにグローバルにインストールできない場合は、次のコマンドを使用して、binarがある同じディレクトリにこれらのライブラリのコピーを配置できます。

 $ ldd test libgc.so.1 => /usr/lib/libgc.so.1 libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 $ cp /usr/lib/libgc.so.1 . $ cp /lib/x86_64-linux-gnu/libpcre.so.3 . $ LD_LIBRARY_PATH=. ./test 


両方のシステムが32ビットまたは64ビットでなければならないことに注意してください。 そうでない場合は、バイナリを再構築するために、Shed Skinを別のシステムにインストールする必要があります。

マルチプロセッシング



meuk.pyというファイルで次の関数を定義したとします。

 def part_sum(start, end): """ calculate partial sum """ sum = 0 for x in xrange(start, end): if x % 2 == 0: sum -= 1.0 / x else: sum += 1.0 / x return sum if __name__ == '__main__': part_sum(1, 10) 


このファイルを拡張モジュールに変換するには、次のように入力します。

  shedskin -e meuk make 


結果の拡張モジュールを標準のマルチプロセッシングライブラリのモジュールで使用するには、Pythonでラッパーを追加するだけです。

 from multiprocessing import Pool def part_sum((start, end)): import meuk return meuk.part_sum(start, end) pool = Pool(processes=2) print sum(pool.map(part_sum, [(1,10000000), (10000001, 20000000)])) 


C / C ++コードの呼び出し



C / C ++コードを呼び出すには、次の手順を実行します。

C / C ++コードタイプモデルを使用して、Shed Skinに入力情報を提供します。 特定の数よりも大きいn個の最小素数のリストを返す単純な関数を呼び出す必要があるとします。 Shed Skinがタイプマッチングを実行するには、stuff.pyファイルに含まれる次のタイプモデルで十分です。

 #stuff.py def more_primes(n, nr=10): return [1] 


実際のタイプマッピングを行うには、このタイプモデルを使用するtest.pyというテストプログラムを作成し、それを翻訳します。

 #test.py import stuff print stuff.more_primes(100) shedskin test 


test.pyの他に、このコードはC ++のstuff.pyも変換します。 これで、stuff.cppファイルにC / C ++コードを手動で記述できます。 test.pyファイルの次の翻訳中に上書きされないように、ものを移動します* lib / Shed Skinディレクトリに移動します。

標準ライブラリ


* lib /に、Shed Skinにカスタムライブラリモジュールのサポートを実際に追加しました。 Shed Skinが放送する他のプログラムは、ライブラリをインポートしてmore_primesを使用できるようになりました。 実際、lib /ディレクトリには、サポートされているすべてのモジュールの型モデルと実装が含まれています。 ご覧のとおり、一部はShed Skinを使用して部分的にC ++に変換されています。

小屋の皮の種類


Shed Skinは、Pythonの組み込み型をC ++のクラスセットで再実装しています。 Pythonの同等物と同じインターフェースを持っているため、使いやすいです(基本的なC ++の知識があると仮定します)。 クラス定義の詳細については、lib / builtin.hppファイルを参照してください。 疑わしい場合は、同様のPythonコードをC ++に変換して結果を確認してください!

コマンドラインオプション



shedskinコマンドは、次のオプションをサポートしています。



たとえば、test.pyファイルを拡張モジュールとして翻訳するには、shedskin –e testまたはshedskin ––extmod testと入力します。

-bまたは--noboundsオプションは、パフォーマンスに大きく影響する可能性がある範囲外の例外(IndexError)を無効にするため、非常に頻繁に使用されます。

  a = [1, 2, 3] print a[5] # invalid index: out of bounds 


生産性のヒントとコツ


ヒント


通常、小さなメモリ割り当て(たとえば、新しいタプル、リスト、またはクラスインスタンスの作成)は、Pythonプログラムの速度をあまり低下させません。 ただし、C ++での翻訳後、それらはしばしばボトルネックになります。 これは、すべてのメモリ割り当てに対して、システムからメモリが要求され、ガベージコレクタによってメモリをクリアする必要があり、その後の多数のメモリ割り当てによってキャッシュが欠落する可能性が高いためです。 高性能への重要なアプローチは、小さな割り当ての数を減らすことです。たとえば、小さなジェネレーター式をループに置き換えたり、一部の計算で中間タプルを削除したりします。

ただし、aの列挙型(..)、a、bの列挙型(..)、a、bのsomedict.iteritems()の慣用的な場合、オプティマイザーによって中間の小さなオブジェクトが破棄され、長さ1の行があることに注意してください。キャッシュされます。

Pythonの一部の機能(生成されたコードの速度を低下させる可能性がある)は、必ずしも必要ではなく、オフにすることができます。 詳細については、コマンドラインオプションのセクションを参照してください。 通常、範囲チェックをオフにすることは非常に安全な最適化であり、インデックスピック操作が頻繁に使用されるコードの場合に非常に役立ちます。

生成されたコード内の属性を介したアクセスは、インデックスによる取得よりも高速です。 たとえば、vx * vy * vzはv [0] * v [1] * v [2]よりも高速です。

シェッドスキンは、シェッドスキンがインストールされているディレクトリのFLAGS *ファイルからC ++コンパイラのフラグを取得します。 これらのフラグは、FLAGSという名前のローカルファイルを使用して変更または変更できます。

多くの浮動小数点計算では、IEEE浮動小数点の仕様に従う必要は必ずしもありません。 -ffast-mathフラグを追加すると、パフォーマンスが大幅に向上します。

プロファイリングにより、パフォーマンスをさらに向上させることができます。 GCCの最近のバージョンでは、最初に-fprofile-generateで生成されたコードをコンパイルして実行し、次にfprofile-useで実行します。

最良の結果を得るには、CPPFLAGS = "-O3 -march = native"を使用してBoehm GCの最新バージョンを構成します。/configure--enable-cplusplus --enable-threads = pthreads --enable-thread-local-alloc --enable-large -config --enable-parallel-mark。 後者のオプションにより、GCは複数のプロセッサコアを使用できます。

最適化する場合、プログラムの各部分に費やされる時間を知ることは非常に役立ちます。 Gprof2Dotプログラムを使用すると、別のプログラムと元のPythonコードの両方で美しいトラフィックを作成できます。 OProfileプログラムを使用して、拡張モジュールのプロファイルを作成できます。

Gprof2dotを使用するには、Webサイトからgprof2dot.pyファイルをダウンロードし、Graphvizをインストールします。 次に:

 shedskin program make program_prof ./program_prof gprof program_prof | gprof2dot.py | dot -Tpng -ooutput.png 


OProfileを使用するには、次のようにインストールして使用します。

 shedskin -e extmod make sudo opcontrol --start python main_program_that_imports_extmod sudo opcontrol --shutdown opreport -l extmod.so 


トリック


次の2つのコードスニペットは同じように機能しますが、2番目のコードのみがサポートされています。

 statistics = {'nodes': 28, 'solutions': set()} class statistics: pass s = statistics(); s.nodes = 28; s.solutions = set() 


関数または印刷ステートメントへの引数が評価される順序はC ++に変換されると変化するため、それに頼らないことが最善です。

 print 'hoei', raw_input() # raw_input    'hoei'! 


現在、要素の種類と長さが2を超えるタプルはサポートされていません。ただし、それらはエミュレートできます:

 class mytuple: def __init__(self, a, b, c): self.a, self.b, self.c = a, b, c 


#{と#}で囲まれたブロックコメントは、Sched Skinによって無視されます。この機能を使用して、コンパイルできないコードにコメントを付けることができます。たとえば、次のスニペットは、CPythonで起動されたときにのみポイントを表示します。
 print "x =", x print "y =", y #{ import pylab as pl pl.plot(x, y) pl.show() #} 

バージョン0.9.4、2013年6月16日、Mark DufourおよびJames Coughlan

Source: https://habr.com/ru/post/J194650/


All Articles