2011年12月9日金曜日

PyPy 1.7 on Win32

原文はこちら: PyPy 1.7 on Win32

やぁ、みんな!

We have fixed _continuation on Win32 (thanks Stakkars), and so we have now a Win32 version of PyPy 1.7.

私はWin32の_continuationを完了する(ありがとうStakkars)とともに、Win32版のPyPy 1.7をリリースしました。

(原文:Posted by Armin Rigo)
(翻訳:Tohru Ike)

2011年11月21日月曜日

PyPy 1.7 - スイートスポットの拡大

原文はこちら: PyPy 1.7 - widening the sweet spot

PyPy 1.7のリリースをアナウンスできることを嬉しく思います。バージョン1.6から、いつも通り多くのバグフィックスとパフォーマンスの改善が行われました。しかしながら、以前のバージョンとは異なり、このバージョンではPyPyの"スイートスポットの拡大"に焦点を当てています。すなわち、PyPyによって高速化できるPythonコードのクラスの幅がとても広がりました。PyPy 1.7はこちらからダウンロードできます。

http://pypy.org/download.html

PyPyとはなにか

PyPyはとても素直なPythonインタプリタで、CPython 2.7とほぼ完全な互換性があります。トレーシングJITを採用したことで高速化を実現しました(pypy 1.7とCPython 2.7.1の性能比較はこちら)。

このリリースは、x86上で動作するLinux 32/64、Mac OS X 32/64、Windows 32をサポートしています。Windows 64は現在作業中ですが、ネイティブではサポートされません。

このリリースのメイントピックは、PyPyが高速化できるコードの幅が広がったことにあります。我々のベンチマークによれば、PyPy 1.7はPyPy1.6と比較して平均して30%以上、いくつかのベンチマークでは20倍以上高速化しています。

ハイライト

  • 多くのパフォーマンスの改善。例を上げればキリがないほど高速化された部分があります。
  • バグフィックスと、CPythonとの互換性の確保。
  • Windowsにおける修正
  • デフォルトでスタックレスを実現しました。しかしながら、多くループはJITに割り込んでしまいます。ですから、スタックレスを基礎としたプログラムの高速化は実現していません。この制約を取り除くための情報がありましたらpypy-devまでコンタクトしてください。
  • PyPyにおけるNumPyの名称がnumpypyに変更されました。numpypyを試すためには、シンプルに、import numpypy as numpy と、プログラムの始めに記述してください。PyPyのnumpyは1.6と比較して飛躍的に進化しています。主な特徴はdtypeの実装にあります。
  • JSONのエンコーダが新しいものに置き換わっています(デコーダではありません)。これはPure Pythonで書かれていますが、CPythonのC拡張を用いたものと比較して、いくつかのケースで2倍高速になっています。これはPyPy 1.6と比較すると20倍以上高速化しています。
  • RPythonモジュールにおけるメモリのフットプリントがいくつか改善されています。これは暗号やtornadoなどにおいてインパクトがあります。
  • cpyextで動くCPython C APIにおいてさらなる進展がありました。

まだ完成していないが、1.8において期待される事項

このリリースでは完成しなかったが、次のリリース 1.8において期待してよいかもしれないこと(これはここで書くべきではないかもしれませんが)。

  • リストの特別な実装。整数/浮動小数/文字列のリストにおいて、array.arrayと同様のコンパクトな実装のブランチがあります。いくつかの応用において、パフォーマンス/メモリ使用量の劇的な改善が見込まれます。
  • NumPyにおいて進行中の成果。多次元配列はすぐ実現するでしょう。
  • ふたつの新しいJITアセンブラのバックエンドがあります。PowerPCとARM向けのものです。

資金調達

ここで触れるべきことではないかもしれませんが、PyPyにおけるNumPyとPython 3のプロジェクトにおいて資金調達のキャンペーンが行われています。この件について素早く情報が欲しいかたは、numpy proposalpy3k proposalをご覧ください。PyPyの進捗に関心があり、全体としての方向性に信頼をおいているならば、genral potへの寄付もご検討ください(寄付は常に受け入れています)。

Cheers,

Maciej Fijałkowki, Armin Rigo and the entire PyPy team

(原文:Posted by Maciej Fijalkowski)

(翻訳:Kentaro Iizuka)

2011年9月4日日曜日

ソフトウェアトランザクショナルメモリが必要です。

原文はこちら: We need Software Transactional Memory

Hi、みんな。ここはPythonやPythonnoような言語の実装における、現時点のソフトウェアトランザクショナルメモリについての論文の短い要約(抽出物)です。

このブログ記事をより良くしてくれたIRCの人々(lucian, Alex Gaynor, rguillebert, timonator, Da_Blitz)に感謝します。
現在の議論の目的のために、私たちはPythonとJavaのマルチスレッド化を比べています。

複雑なハイレベル言語における問題

Javaのように、Python言語は保証します: Python仮想マシンはスレッドの誤った使用が原因でクラッシュすることは受け入れられません。
Javaのプリミティブ操作は、フィールドのオブジェクトフィールドを読み書きするようなものです; 対応する保証は、方針に沿っています。
プログラムがオブジェクトのフィールドを読み取り、別のスレッドが同じオブジェクトの同じフィールドに書き込む時、プログラムは、古い値または新しい値を参照するでしょう。
しかし、全く違った値ではなく、仮想マシンはクラッシュしません。Pythonのような高級言語は、"プリミティブな操作"は、はるかに複雑であるという事実があるため、Javaとは異なります。
それは例えば、いくつかのハッシュマップでおそらくアップデートを行って見られるかもしれません。
一般的には、それはすべての操作をシングルプロセッサの命令にアトミックに必ずし、完全にマップするのは不可能である。

JYTHON: きめ細かいロック

この問題はJavaの上で動作するJythonインタプリタでは"明示的に"解決されています。
解決策は次の文の通り明示的です:Jythonインタプリタ全体を通して、一つ一つの操作は、Javaレベルのロックメカニズムを慎重に利用しています。これは、"きめ細かいロック"のアプリケーションです。例えば、属性をハッシュマップの数だけ参照する操作はロック(__getattribute__における)の獲得と解放により保護されています。

このソリューションの引っ込めは、必要な細部へのこだわりです。一箇所でもロックをミスしたのであれば、いずれかのバグがあります。 --- このようなバグは、以前のバグが修正されているようにデバッグするため、ますますまれにしか起こらず困難なケースで発生します ---

また、私たちは"CPythonとの違い"のもとでそれを適正に保存します。しかしながら、二つのスレッドが異なる順序で同じオブジェクトをロックしようとすると、デッドロックの危険性があります。実際には、予想に反して私が手書きするほど悪い状態ではありません。Jythonにおけるロックの数は適切であり、そして、期待どおりに動作するすべての"一般的なケース"が可能になります。(稀なケースは、以下を参照してください。)

性能面では、Java仮想マシン自体は、長期間にわたって猛烈な最適化されているロックが付
属しています。
しかしながらCでコーディングされたソリューションの場合は、手動でロックを最適化するために余分な作業が多く必要になります(多くのバグを含んでしまうだろう)。

CPYTHON: きめのあらいロック

CPython、C言語によるPythonの標準実装では異なる単純なアプローチを取った:それはシングルグローバルロックを持ち、グローバルインタプリタロック(GIL)と呼ばれる。
それは"きめのあらいロック"を使用しています: ロックは、全体の実行であっちこっちにある1つのバイトコードが取得されて、リリースされます(実際に100のような少ない数のバイトコード)。それらを呼び出す複数バイトコードはGILで自分自身をシリアライズされるので、この解決策は、2つの操作が互いに競合しないことを保証するのに十分です。それは、インタプリタ全体にわたって慎重なロックを獲得するコードを書くのを避ける --- Jythonとは異なった ---解決策です。また、より強い保証を提供しています:すべてのバイトコードは、アトミックに完全に実行されます。

今日において、GILのアプローチの引き戻しは、マルチコアマシン上では明白です:複数のスレッドを開始し、バイトコードの実行をシリアル化することによって、実際には複数のコアのインタプリタの使用を許可しません。

PyPy、PythonによるPythonの実装では、これまでと同じアプローチを採用しています。

現在の処理

これまで見てきたように、我々は次のような解決策:既存のPython言語、CPython実装は、マルチスレッドの使用状況についての非常に強い保証を提供しています。
それは、ほとんどの既存のマルチスレッドのPythonプログラムが実際にそのような強い保証に頼ることは重要であると強調する。
これは、人口のリストを受け取り、いくつかのスレッドで行う問題の例で見ることができます:

next_item = global_list.pop()

これは、暗黙的にpop()がリストからアトミックの除去を実行するという事実を当てにしています。
二つのスレッドが同時に同じリストからポップしようとすると、2つの操作は、ある順序または別で発生します。
しかし、それらは例えば、両方のスレッドへ同じオブジェクトを返すか、リストオブジェクトの内部状態までぶち壊します。

このような例を念頭に置いて、私たちはこれらのマルチコア問題を含む強力な保証を落とす解決策を望んでいないことは明らかです。
しかしながらJythonがそうするように障壁を下げるのは、間違いありません; しかし、いくつかのPython実装は、いくつかの保証を提供しなくてはいけません、または、マルチスレッド化を全く提供してはいけません。
これは、組み込み型のメソッドの多くがアトミックであるという事実が含まれています。

(それは、全てにおいてマルチスレッドを提供していないことも実際にはまた、(部分的な)問題の解決策であることに留意すべきです。
最近、いくつかの"ハック"は、多かれ少なかれ透過的なアクセスし複数の独立したプロセス (e.g. multiprocessing).をプログラマに与えることが明らかです。
これらはいくつかのコンテキストで適切な解決策を提供するわけだが、マルチスレッドのように広く適用されません。
典型的な例として、それらは、複数のコアが、全くシリアライズできない情報を処理する必要があるとき適用しません。
--- いくつかのプロセス間における、いかなるデータ交換の要件。)

ここに、Jythonの一貫性がCPythonのGILよりどう弱いかの例があります。
それを示しているのに一般的でない例を要しており、CPythonのようなプログラマがそれらを予想するどんな仕事もせず、一般的に、実装の詳細として考えられている。
考察:

Thread 1: set1.update(set2)
Thread 2: set2.update(set3)
Thread 3: set3.update(set1)

各操作はCPythonの場合にはアトミックです。
しかし、Jythonの場合は、次の2つの手順(各々は、アトミックとみなすことができる)に分けられる:引数から読み取り、そして、対象のsetを更新する。
最初にset1 = {1}, set2 = {2}, set3 = {3} を行うとします。CPythonにおいて、独立したスレッドの実行順序の結果、最も小さなset {1, 2, 3} になります。
Jythonにおいて、すべての3つのsetが2つだけの要素を含むだけで終わっている可能性があります。
例は、少しこじつけですが、CPythonとの一貫性はJythonのより厳密に強くなって表示されるべきです。

PYPY

PyPyはCPythonやJythonに非常に似たPythonインタプリタです。しかし、生成方法は独特です。
それはPythonのサブセットであるRPythonで書かれたインタプリタであり、自動的に"翻訳"と呼ばれる段階で完全な仮想マシン(Cのコードを生成)になって手に入ります。
このコンテキストでは、CPythonとJythonのコンテキストはは異なります: PyPyではまったくもって簡単に、"翻訳時"にインタプリタへ任意のプログラム全体の変換を適用することが可能です。

この点を考慮し、インタプリタによってRPythonで操作の全オブジェクトにロックを追加し、プログラム全体の変換を想像することは可能です。
これはJythonと同様な状況として終わるでしょう。
しかしながら、それは慎重な手動配置でのロックがJythonの場合は回避され、デッドロックの問題が自動的に解決するとはなりません。
(実際には、デッドロックフリーであることは自動的に確保、検証できないグローバルプログラムのプロパティです;
いくらかのJythonの変更は、理論的にはこのプロパティを破るため、巧妙なデッドロックを導入することができます。
同じことが、ノンアトミックに適用されます。)

実際には、もし、インタプリタがスレッド1のバイトコードでAとB、およびスレッド2のバイトコードでBとA(反対の順序)に(読み取りおよび書き込みの両方の)アクセスした場合、私たちは簡単にそれを確認することができ、
--- また、あなたが2番目のオブジェクトにアクセスする必要がある、と決める前に最初のオブジェクトにアクセスしている必要があります。 ---
その時、アトミック性の強い保証を維持しながらデッドロックを(GILから離れて)回避する方法はありません。
確かに、両方のスレッドがそのバイトコードの実行の途中に進行した場合は、既にスレッド1によって変更されており、同様にBは既にスレッド2によって変更されています。
その場合、うまくスレッドを実行し続けるのは、可能ではありません。

USING SOFTWARE TRANSACTIONAL MEMORY

ソフトウェアトランザクショナルメモリ(STM)は、明らかに上述の問題に解決策をもたらすアプローチです。
実行し続けるどこのスレッドが間違った状況で終わった場合、私たちは、中断しロールバックすることができます。
これは、データベースのトランザクションの概念と似ています。
上述の例において、一方または両方のスレッドは、トラブルや中断を実行しようとしていることがわかります。
これは、彼らはこれまでに中止または単にまだコミットされていないものすべての副作用において、バイトコードの先頭で実行を再開する方法を持っている必要があることをより、具体的に意味しています。

私たちは、中断してロールバックする能力はPythonのマルチスレッド実装に欠けているパズルのピースだと考えています。
実際に、上記の問題のプレゼンテーションによれば、それはCPythonの一貫性とアトミック性の同じレベルを提供したすべての解決策は、中断とロールバックの容量を伴うことは避けられないです。--- これは正確にそのSTMを避けることができないことを意味します。

OK、しかしなぜJythonのアプローチで落ち着いて、インタプリタを通して左右の慎重なロックをかけないのか?
(1)私たちは、すべての操作のアトミック性を考慮し、決定を確認する(またはJythonのを盗む)必要がありますし、それらをここに文書化します;
(2)それはまた、これらのロックを最適化するために、本当に多くの作業となります。e.g. JITのみならずJVMが行うことと同様に;
(3)それは直交すべき機能のどこにでも手動で微調整するコードを必要とし、それはPyPyの方法ではありません;
(3)はおそらくここで最も重要です:あなたはPyPyで実装する言語ごとに作業をやり直す必要があります。
私自身もまたポイント(4)を意味する:それは楽いことではない :-)

詳細であるが、次のようにプロセスは働くだろう。
(これは、1つの可能なモデルの概要を説明します; 異なったモデルがより良くなることは確実です。)すべてのスレッドで:
  • バイトコードの開始時に、私たちは"トランザクション"を起動します。これは、トランザクションで発生するログを記録するために、スレッドローカルなデータ構造を設定することを意味します。
  • 私たちは、ログに読み込まれるすべてのオブジェクトを、行いたい変更と同様に記録します。
  • この時間の間に、私たちは"読み取り"の不整合を検出し、現在のトランザクションの開始時間より遅いオブジェクトの"最終修正"タイムスタンプによって証明し、また中断する。
  • これは、コードの残りの部分が矛盾した値とともに実行されるのを防ぐことができます。
  • 私たちは"読み取り"矛盾なくバイトコードの最後に到達した場合は、私たちは、アトミックに"書き込み"の不整合を確認してください。
  • これらは、他のスレッド内のオブジェクトへの同時更新から生じる矛盾です。---「書き込み」という私たちのオブジェクトか「読み込み」という私たちのオブジェクトのどちらか
  • 不一致がまったく見つからない場合、私たちは、トランザクションを遅延をコピーすることによって、メインメモリにログから書き込み、"コミット"します。

どれの取引が始まるか、終わりがまさにポイントであるか指す、どれですか?
トランザクションが開始または終了するポイントは正確なポイントで、CPythonでは、グローバルインタプリタロックは、それぞれ取得され、解放されます。

私たちであれば(純粋に性能のために)CPythonがGILを取得して、Nバイトコードごとにリリースするという事実を無視してください。その時これを意味しています。
  1. 私たちは、任意のバイトコードの前にGILを(トランザクションを開始する)を取得し、バイトコードの後に私たちは、それ(トランザクションを終了する)を放します; そして
  2. CライブラリやOSへの外部呼び出しを行う前に、私たちは、GIL(トランザクションを終了する)を放し、その後のそれ(次のトランザクションを開始する)を再取得します
特にこのモデルは、システムコールと同様に、私たちは --- まさに --- システムコールのようにロールバックすることができないトランザクション以外に何もできず、STMの条件に適しています。
確かに、構造によって、CPythonではそれらはGILの解放で発生するため、これらのシステムコールはトランザクションの外で発生します。

PERFORMANCE

今のところ、多くの詳細な実装はまだ未解決です。
ユーザーの観点(i.e. Pythonを使用してプログラマ)から、最も関連しているものは総合的なパフォーマンスの影響です。
私たちは、これまで正確な数値を与えることはできず、また、初期性能がものすごく悪いと予想しています。(10倍遅いかもしれない);
しかしながら、ロック機構、ロックを挿入するグルーバルプログラムの変換、ガーベージコレクション(GC)、ジャストインタイムコンパイラ(JIT)への連続な改善において、私たちはそれがほぼ妥当なパフォーマンス(たぶん、最大2倍遅い)を得ることが可能であると信じています。
例えば、GCは発生したスレッドから漏れることなくオブジェクト上のフラグを保持する。
私たちは、これらが多くの失われた性能を取り戻すことができる最適化の一種であると信じています。

THE STATE OF STM

トランザクショナルメモリは、Tom Knightにより1986年の論文で配信され、それ自体は比較的古い考え方です。
最初はハードウェアサポート、1995年に大衆化されたソフトウェアのみのトランザクショナルメモリ(STM)の考え方に基づいており、最近では集中的な研究の焦点となっています。

上記で説明したアプローチ --- 言語の実装の中核を形成するためにSTMを使用 --- は、私たちの知る限り、新しいです。
これまでのところ、ほとんどの実装ではライブラリの機能として、STMを提供しています。

それは明示的に使用する必要があります。多くの場合、オブジェクトは、STMによって保護されている必要があり、明示的に宣言の形態であってもよい(オブジェクトベースTSTMs)。
STMのネイティブサポートがClojure言語で特に現れ始めたのは最近のことです。

STMは、アプローチとして、Wikipediaに "大幅にマルチスレッドプログラムの概念的な理解を簡素化し、そのようなオブジェクトやモジュールなど既存の高レベルな抽象化と調和して働くことによって、プログラムのメンテナンス性の向上に役立ちます。" と記載されています。
私たちは実際に、これらの利点は、内部的に使用されているだけでなく、同様にPythonプログラマに公開されているほど重要であると考えています。

これは、Pythonプログラマは非常にシンプルなインターフェイスを与えるだろう:

with atomic:
<これらの操作はアトミックに実行されます>

(これは古い考え方です。私を含め2003人の人々はこれを面白いハックだと考えました。
いま私がブログ記事を書き、"それはハックではなかった、それは明確にハックされたロックを使用している。"と主張します。私は、composabilityのアイデアを買っています。)

実用的な観点から、私はロチェスター大学STM(RSTM)において、最近の研究の焦点となっているC++ライブラリ --- と結果のコレクション --- を真剣に探し始めた。
特に代表的な論文の一つが、Michael F. Spear、Luke Dalessandro、Virendra J. MaratheとMichael L. Scottによる
A Comprehensive Strategy for Contention Management in Software Transactional Memoryです。

CONCLUSION

これらのアイデアを取り、Pythonのような複雑な高水準言語の実装のコンテキストでこれらを適用すると、独自な難問が出てきます。
このコンテキストでは、PyPyを使用することは、実験プラットフォーム、また最近ではそのパフォーマンスのために注目されているプラットフォームにおいて道理にかなっています。
代替手段は興味ありません:例えばCPythonのでそれを行うと、グローバルインタプリタを書き換えることを意味します。
PyPyは代わりに、私たちは翻訳時に体系的に適用される変換としてそれを書きます。
また、PyPyは動的言語のために高速なインタプリタを生成するための一般的なプラットフォームです; PyPyにおいてSTMの実装だけではなく、同様にPython用だけではなく、他の言語の実装のために、Pythonの制約から抜け出して動作します。

Update:

これはほとんど私(Armin Rigo)が声を出して暴れる実験をしています; PyPyチーム全体が現在それに取り組むために、次の数年をフルタイムで費やすことを意味する記事と混同するべきではありません。
私が述べたようにそれは実際のPythonインタプリタに直交しており、それはいかなる場合でも、翻訳時にオンまたはオフにすることができる機能です;
私が知っている多くのまたはほとんどのユースケースでは、人々は2倍遅くしかしスケールするものより、むしろ速いPyPyに興味を持ち、手に入れたいと考えています。
私が言ったものはまったく本当に新しくありません。
証明のため、Riley and Zilles (2006)Tabba (2010) の両方は、私がここで説明したように、開始/終了のトランザクションにCPythonのかPyPyインタプリタのGILを回し、ハードウェアトランザクショナルメモリを試したので、参照して下さい。

(原文:Posted by Armin Rigo)
(翻訳:Tohru Ike)

2011年8月19日金曜日

PyPy 1.6 - kickass panda

原文はこちら: PyPy 1.6 - kickass panda

私たちは、PyPy1.6のリリースを発表できることを嬉しく思います。このリリースでは、多くのバグ修正と1.5以上のパフォーマンスの向上をもたらし、Windows32とOS X 64bitのサポートの向上をします。このバージョンでは、Python2.7.1を完全に実装し、CPythonのC拡張読み込みをベータレベルサポートしています。ここからダウンロードできます。

http://pypy.org/download.html

PyPyとは?

PyPyは、非常に準拠しているPythonインタプリタであり、ほぼ、簡単にCPython2.7.1を代替できます。それはトレーシングJITコンパイラを統合しているため、高速です。(pypy1.6とCPython2.6.2のパフォーマンス比較

このリリースでは、x86マシンで実行しているLinuxの64/32または、Mac OS Xがサポートされています。Windows 32はベータです(ほぼ動作しますが、多くの小さな問題はこれまで修正されていません)

このリリースの主なトピックは、スピードと安定性です: 私たちのベンチマークスイートの平均において、PyPy1.6は既に私たちのベンチマークではCPythonより高速なPyPy1.5より20%から30%速いです。

速度の改善はPyPyを構成する多くの層を最適化することで実現しました。
とりわけ、私たちはガーベージコレクタ、JITのウォームアップ時間、JITによって実行される最適化、生成されたマシンコードの品質とPythonインタプリタの実装を改善しました。

ハイライト

  • 多数のパフォーマンス向上は、全体にかなりのスピードアップをもたらします:

    • 非常に大きなオブジェクトや配列を扱う時のGCの振る舞い。

    • fast ctypes:現在呼ばれるctypes関数はJITによって最適化され、PyPy1.5より60倍高速で、CPythonより10倍高速である。

    • ジェネレータの改良(1): 単純なジェネレータは現在、ループ呼出しをインライン展開しており、PyPy1.5より3.5倍高速化し、パフォーマンスアップしました。

    • ジェネレータの改良(2): 他の最適化のおかげで、ジェネレータはインライン化されてなくともPyPy1.5より10%から20%速いです。

    • JITの高速なウォームアップ時間。

    • single floatのJITサポート (e.g., for array('f'))。

    • 最適化された辞書: 辞書の内部表現は、現在、動的に保存されているオブジェクトの種類に応じて適宜選択します。高速なコードと小さなメモリフットプリントの結果です。例えば、キーがすべて文字列、またはすべて整数である辞書。 他の辞書でもバグ修正のため、より小さくなります。

  • これはあなたのPythonコードがJITによってコンパイルされアセンブリまで落とし込まれたものを確認するウェブベースツールであるJitViewerを含む最初の公式リリースです。
    jitviewer 0.1は既にリリースされておりPyPy1.6でうまく動いています。

  • CPythonの拡張モジュールAPIが改善され、より多くの拡張機能をサポートしています。何がサポートされているかの情報については、私たちの互換性wikiを参照してください。

  • マルチバイトエンコーディングのサポート:これはCPythonが原因となって残っていた最後の領域でした。しかし、今私たちはそれを完全にサポートしています。

  • numpyのための準備的サポート:このリリースでは、PyPy JITと統合された非常に高速なnumpyのモジュールのプレビューが含まれています。残念ながら、これは、あなたが既存のnumpyのプログラムを作って、PyPyでそれの実行を期待できるということを意味するものではありません。なぜならば、モジュールはまだ未完成であり、numpyAPIのいくつかをサポートしているためです。しかしながら、いくつかの詳細を除いて、その仕組は非常に高速でなければなりません:-)

  • バグ修正: 1.5リリース以来、私たちはバグトラッカーにある53のバグを修正しました。私たちが発見し、バグトラッカー以外の経路を介して報告された多くののバグはカウントしていません。

喝采を贈る

Hakan Ardo, Carl Friedrich Bolz, Laura Creighton, Antonio Cuni, Maciej Fijalkowski, Amaury Forgeot d'Arc, Alex Gaynor, Armin Rigo とPyPyチームへ

(原文:Posted by Maciej Fijalkowski)


(翻訳:Tohru Ike)

2011年8月15日月曜日

JITされたコードの視覚化

原文はこちら: Visualization of JITted code

こんにちわ。

私たちはjitviewerの最初の公式リリースを発表できることを誇りに思います。
現在のところ、jitviewerはPyPyのJITによってマシンコードにコンパイルされた
Pythonソースコードの理解を助けるちょっとした内部ツールです。

インストールするには、PyPyのごく最近のバージョン(8月9よりも新しい)、例えば、ナイトリービルドのいずれかが必要です:

  • pipとdistributeをインストールするまたは、PyPyで作られたvirtualenvか、次の述べるinstallation instructions

  • PyPyのソースコードチェックアウトし、PYTHONPATHに確実に入れてください。

  • pip install jitviewer。グローバルにインストールされたpipではなく、PyPyの管理下にあるpipを実行する必要があることに注意して下さい。


jitviewerをはじめるにはREADMEを見るまたは、それをただ実行したいならオンラインデモを試みてください。

jitviewerはflaskとjinja2で書かれたWebアプリケーションです。
ウェブ開発経験があって、PyPyを助けたいなら、私たちに遠慮無く連絡してください。PyPyを向上する多くのことがあります :-)

本当のところjitviewerは何をするのでしょうか?


ページの上部には、JITでコンパイルされたコードの一部分のリストが表示されます。
あなたは通常のループと"エントリブリッジ"の両方を確認することができます。ここでは、それらの違いについて議論する適当な場所ではありませんが、たぶんあなたは、通常はほとんどの時間が費やされている場所なので、ループをもっとも見たがっているでしょう。

各ループにおいて、ループの最初の命令を含んだ関数の名前が表示されることに注意してください。
しかしながら、JITによって行われるインライン化のおかげで、それに他の関数のコードが含まれます。

あなたがひとたびループを選択すると、jitviewerはJITがどのような階層的な方法でPythonソースコードをアセンブラにコンパイルしたのかを示します。それは4つのレベルで表示します:

Pythonソースコード: 空色で示された行だけが、この特定のループのためにコンパイルされており、灰色のものはそうではありません。

Pythonバイトコード, あなたが実行することによって得るもの:
def f(a, b):

return a + b
import dis
dis.dis(f)
命令コード(opcodes)は、例えば LOAD\_FAST, LOAD\_GLOBAL などです。
太字になっていない命令コード(opcodes)は、完全にJITで常に最適化されています
  • JITコードの中間表現(IR)。これは操作(整数加算のような、構造体のフィールド読み出し)とガード(私達の仮定が実際に真であることを確認する)の組合せです。ガードは赤色です。それらの操作は"Cと同じレベルで": そう、例えば、+は、CPUのレジスタに格納された2つのボックス化が解除された整数を取ります。

  • アセンブラ: あなたは、右上メニューの"Show assembler"をクリックして、それを確認することができます。


時に、ガードが頻繁に失敗し、アセンブラの新しい部分が呼ばれてコンパイルされた事がわかるでしょう。
これはコードを通した迂回経路であり、そして、それはブリッジと呼ばれます。
ガードの横にリンクがある時には、jitviewerでブリッジを見ることができます。

詳しい情報の成果を得たいのであればjit documentationを探して下さい。

いまだに混沌としています


私が現状について説明すると、Jitviewerは完全ではありません。
あなたが、IRC上に現れたり、私たちのメーリングリストへメールを送ることは自由であり、また私たちはそれを説明し、解決策を改良するでしょう。
詳細については、お問い合わせのページを参照してください。

Cheers,
fijal & antocuni

(原文:Posted by Maciej Fijalkowski)

2011年8月3日水曜日

PyPyはCより速い。あらためて:stringフォーマッティング

原文はこちら: PyPy is faster than C, again: string formatting

stringフォーマッティングは、おそらくPythonであなたが通常することであり、あえて考えたりはしないでしょう。
それはとても簡単で、まさに"%d %d" % (i, i)すれば完了です。
結果のバッファをどのように測るかは考えません。あなたの出力の最後に適したNULLバイトを持っているようと、または他の細かい事があろうと。Cに相当するのは次のようになります:
char x[44];
sprintf(x, "%d %d", i, i);

2行目のところで立ち止まり、数がどれくらい大きくなるかもしれないかを考慮して、サイズを過大評価しなければならなかったことに注意してください。
(44 = 64ビット上で最も大きい数の長さ(20) + 1 (符号のための) * 2 + 1 (空白のための) + 1 (NULバイト))
この投稿の著者、fijalとalexはこの3つの計算の試みを異議なしで実現しました :-)

この関数からxを返すことができない場合を除きすばらしいです。よりよい比較は次のようになります。
char *x = malloc(44 * sizeof(char));
sprintf(x, "%d %d", i, i);

xはいくらかの状況でわずかに過剰割り当てされますが、すばらしいです。

しかし、ただstringフォーマッティングの実現について議論するために、私たちはここいるわけではなく、速いPyPyを明確に新たなunroll-if-altブランチとともに示し、議論するためにここにいます。Pythonは次のコードをもたらします:
def main():
for i in xrange(10000000):
"%d %d" % (i, i)

main()

Cコード:
#include 
#include


int main() {
int i = 0;
char x[44];
for (i = 0; i < 10000000; i++) {
sprintf(x, "%d %d", i, i);
}
}

PyPyのunroll-if-alt branchブランチのheadで実行し、コンパイルはGCC4.5.2 -O4(他の最適化レベルをテストし、これが最高のパフォーマンスをもたらした)。PyPyで実行すると0.85秒、コンパイル済みのバイナリで実行すると1.63秒かかりました。
私たちはこれは動的コンパイルの信じられないほどの可能性を示すと考えおり、GCCはsprintfの呼び出しをインライン化またはアンロールすることができません。理由はlibcが内部に位置しているためです。

Cのベンチマークコード:

#include
#include


int main() {
int i = 0;
for (i = 0; i < 10000000; i++) {
char *x = malloc(44 * sizeof(char));
sprintf(x, "%d %d", i, i);
free(x);
}
}

前に議論されたとおり、これはPythonに匹敵し、1.96秒の結果が得られます。

パフォーマンスの結論:
PlatformGCC (stack)GCC (malloc)CPythonPyPy (unroll-if-alt)
Time1.63s1.96s10.2s0.85s
relative to C1x0.83x0.16x1.9x

全体的にPyPyは2倍速いです。これは明らかに、静的より動的コンパイラが優っています。 - sprintf関数はlibcにあり、不変な文字列に特化することができず、実行されるときはつねに、文字列を評価しなければなりません。
PyPyのケースでは、私たちは、剰余演算子の左側の文字列が不変であると検出すれば、アセンブラに特化することができます。

Cheers,
alex & fijal

(原文:Posted by Maciej Fijalkowski)

2011年7月8日金曜日

Pythonでのリアルタイム画像処理

原文はこちら: Realtime image processing in Python

画像処理は、CPUを酷使する悪名高い仕事です。
ですので、リアルタイムで画像処理をするのにPythonで試みるのは馬鹿げており、高速な言語でアルゴリズムを実装する必要があります。
Pythonは明らかにこのタスクに十分な速度がありません。そうですよね?

実際には、画像処理において、PyPy JITコンパイラはリアルタイムのビデオ処理を行うために十分な速さのコードを生み出すことが判明しました。

sobel.pyは、画像、Sobel operatorのエッジを強める古典的な方法を実装しています。それは、グラデーションの大きさの近似値です。

処理時間は画像と3x3カーネルの間の2つの畳み込み処理に費やしています。
この処理は、入力画像を出力画像へピクセルを越えてコピーするシングルループで構成されています。

あなたは適切ななデモをダウンロードしてあなた自身で試すことができます。
  • pypy-image-demo.tar.bz2: このアーカイブはソースコードのみを含んでいます。これは既にインストールされているPyPyを必要とします。
  • pypy-image-demo-full.tar.bz2: このアーカイブは、Linux32bitと64bitのソースコードとビルド済みのPyPyバイナリの両方が含まれています
デモを実行するには、MPlayerがシステムにインストールされている必要があります。デモはLinux上でのみテストされて、それが他のシステムでも動作する(か動作しないかもしれません)可能性があります。

$ pypy pypy-image-demo/sobel.py

$ pypy pypy-image-demo/magnify.py

デフォルトでは、2つのデモはサンプルのAVIファイルを使用します。
より多くの楽しみを得るために、例に従いMPlayerに適切なパラメータを渡すことで、ウェブカメラを使用することができます。

$ pypy demo/sobel.py tv://

デフォルトではmagnify.pyは近傍法を使用します。
オプション-bを追加することにより、バイリニア補間が代わりに使用され、より滑らかな結果が与えられます。

$ pypy demo/magnify.py -b

magnify.pyのアルゴリズムの実装はただ一つしかありません。
二つの異なった挿入メソッドは実装されます。クラスのサブクラス化に
画像を表現し、補間を埋め込むピクセルアクセスメソッドの内側でよく使われます。

PyPyは画素アクセス法をインライン化し、アルゴリズムの実装を特化できるので、良好なパフォーマンスをこの抽象化で達成することができます。
C++では、画素アクセス法のようなものは仮想的になり、実行時のオーバーヘッドを招くことなく同じ効果を得るためにテンプレートを使用する必要があります。





上記のビデオは、PyPyとCPythonが並んでsobel.pyを実行しているところです。
(PyPyはウェブカメラから入力を受け、CPythonはテストファイルから入力を受けます)
また、PyPyがCPythonよりどのくらい高速であるか実感したいならば、後でデモを走らせて下さい。これらは、上記tarballリンクの標準のtest.avi ビデオとPyPyプレビルドバイナリを使用した際のマシン((Ubuntu 64 bit, Intel i7 920, 4GB RAM))での平均したfps(frames per second)です。
sobel.pyの場合:
  • PyPy: ~47.23 fps
  • CPython: ~0.08 fps
magnify.pyの場合:
  • PyPy: ~26.92 fps
  • CPython: ~1.78 fps
これはsobel.pyでは、PyPyが590倍速いことを意味しています。
magnify.pyでは、それほど違いは明白ではなく高速化は"たった"15倍です。

このことはPyPyができる極端な例であることに注意しなければいけません。
とりわけ、期待してはいけません(まだ :-))

これがPyPyができることの極端な例であることに注意しなければなりません。 特に、あなたは予想できません(まだ(^o^))。 リアルタイムのときに任意のビデオ処理アルゴリズムを走らせるほど速いPyPy、デモだけがPyPyにはそこに到着する可能性があるとまだ立証しています。
PyPyに、リアルタイムで任意のビデオ処理アルゴリズムを実行するのに十分な速さを期待することはできません。しかしデモの結果は、PyPyが十分な速度を達成する可能性を秘めていることを証明しています。

(原文:Posted by Antonio Cuni)

2011年6月30日木曜日

グローバルインタプリタロックを無くす方法

原文はこちら: Global Interpreter Lock, or how to kill it

EuroPythonで私(Armin Rigo)のライトニングトークを聴いた人は、突然知ったでしょう。我々はグローバルインタプリタロック---悪名高いGIL、複数のスレッドが実際にPythonコードへ干渉するのを防ぐCPythonのものです ---を取り除く計画を持っています。

それは画期的なものではありません。なぜならJythonは既にGILを取り除くことに対応しています。
Jythonは非常に慎重に全ての変更可能な組み込み型にロックを追加し、それらに対して、効率的になるよう基盤にあるJavaプラットフォームを頼っています。(結果は非常に慎重にCPythonで同様なロックを追加するより速いです)
"非常に慎重"は本当の本当に慎重を意味します。
例えば、'dict1.update(dict2)' はdict1とdict2の両方をロックする必要があります。しかし、あなたが安易にしてしまうなら、'dict2.update(dict1)'はデッドロックを生じる可能性があります。

PyPy、CPython、IronPythonとすべてはGILを持っています。
しかし、我々はPyPyに対して、Software Transactional Memory(ソフトウェアトランザクショナルメモリ)に基づく、Jythonとは大きく異なるアプローチを検討しています。
ソフトウェアトランザクションメモリはコンピュータサイエンスにおける近年の研究成果であり、ロックより良い解決策を提供します。
ここでソフトウェアトランザクションメモリについて、簡単に紹介します。

あなたが"list1"から項目をpopして"list2の'にpopしたものを追加したいとしましょう​​:

def f(list1, list2):
x = list1.pop()
list2.append(x)

これは、マルチスレッドとして安全な例ではありません(GILであるとしても)。
つまり、スレッド1でf(l1, l2)とスレッド2でf(l2, l1)を呼び出した場合です。

あなたが望む動作として、全く効果がないということです。(xは一方のリストからもう一方のリストまで動かされた後、元に戻ります)
しかし、それは、二つのリストのトップは入れ替わる代わりに、タイミングの問題によって起こることがあります。

これを治す一つの方法は、グローバルロックを使用することです:

def f(list1, list2):
global_lock.acquire()
x = list1.pop()
list2.append(x)
global_lock.release()

これを修正するより良い方法は、リストが備えているロックを使用することです:

def f(list1, list2):
acquire_all_locks(list1.lock, list2.lock)
x = list1.pop()
list2.append(x)
release_all_locks(list1.lock, list2.lock)

2番目の解決策はJythonのモデルです。そしてもう一方の1番目はCPythonのモデルです。CPythonインタプリタでGILを獲得し、一つのバイトコード(実際は100のような実数)にし、そしてGILを解放します。
そしてさらに、100の次の分岐を続けます。

ソフトウェアトランザクションは3番目の解決策をもたらします:

def f(list1, list2):
while True:
t = transaction()
x = list1.pop(t)
list2.append(t, x)
if t.commit():
break

この解決策では、トランザクションオブジェクトを作成し、すべてのリストへの読み書きで使用します。
それはは実際にいくつかの異なるモデルがありますが、そのうちの一つに注目してみましょう。
トランザクション中に、我々は実際にはまったくもってグローバルメモリを変更しません。その代わりに、スレッドローカルトランザクションオブジェクトを使用します


我々はそのオブジェクトに、どれから読むか、どのオブジェクトに書くか、何の値を書くか格納します。
それは、トランザクションに達した時、私たちはコミットの試みに達するのみです。
コミットは失敗するかもしれません。もし他のコミットがその間に発生した場合、矛盾が発生します。その場合、トランザクションは中止し、再度はじめから処理をやり直さなければなりません。

前の2つの解決策がCPythonとJythonのモデルであるのと同様に、STMの解決策は、PyPyの将来におけるモデルであるかのように見えます。
PyPyインタプリタは、トランザクションを開始、複数のバイトコードを実行、トランザクションを終了という処理をとてもよく繰り返します。
これはCPythonのGILが行っていることと大変似通っています。
特に、この処理はプログラマがGILの場合と同様に、同一の保証を与えられることを意味します。
唯一の違いは、コードがお互いを妨げない限り、マルチスレッドで並列に実行できるということです。
(とりわけ、あなたが既存のマルチスレッド化されたプログラムにおいて、GILだけではなく実際のロックも必要とするなら、これは不思議にもそれらの必要性を削除しません。
あなたがロックよりそれを好むなら、PythonプログラムにSTMを触れさせる特別な組み込みモジュールを取得する可能性がありますが、それは別の質問です)

なぜそのアイデアをCPythonに適用しないのでしょうか?
なぜならば、私たちはいたるところを変更する必要があるからでしょう。先程の例で説明すれば、
私はもう 'list1.pop()'と呼出しをしません。必ず呼出しは'list1.pop(t)'です。これはすべて実装を"トランザクション的な"動きを行うために、変える必要があると言うことです。

これは、代わりに実際にリストが格納されているグローバルメモリを変更することを意味します。つまり、代わりのトランザクションのオブジェクトの変更を記録しなければなりません。
私たちのインタプリタがCPythonのようにC言語で記述されている場合、我々は、明示的にいたるところでそれを書く必要があります。
それはより高いレベルの言語ではなく、書かれている場合など、変換ルールのセットとして、PyPyはそのまま、次に我々はこの動作を追加することができ、それが必要な場所であればどこでもそれらを自動的に適用されます。

それがPyPyのようなより高いレベルの言語で代わりに記述されているのであれば、
我々は変換規則のセットとして、この振舞いを加えることができますとともに、自動的にどこにでも、変換規則が必要であるところに振る舞いを適用します。
また、変換時のオプションを指定することができます。
あなたはGILがある現在の"pypy"、または、余分な出力処理のためより遅いでしょうがSTMがあるバージョンを得ることができます。
(どのくらい遅いかって?私はさっぱりわからないけど、荒っぽい推測として、2〜5倍程度遅いかもしれません。
十分なコアがある場合は、その限りスケールしすばらしいですよ。 :-)

最終的な注記:STMの研究は、ごく真新しく(2003年頃開始)、多くの亜種があり、どの場合にどれがより優れているかは、まだ明らかではありません。
私が判断できる限り、"A Comprehensive Strategy for Contention Management in Software Transactional Memory"(日本語注記:ソフトウェアの取引のメモリにおける主張管理のための包括的戦略)で説明されたアプローチは、1つが最先端であるということであるように思えます。それはまた、"すべてのケースに十分良い"ように思えます。

それで、いつグローバルインタプリタを取り除くことをするのでしょうか?私はまだ明言することができません。
それはまだアイデアの段階ですが、私はそれが実現できると思います。それを書くには私たちはどのくらいの時間を取るでしょうか?
再びさっぱりわかりません、しかし、我々は何日ではなく何ヶ月であると調べています。
これは私がユーロの資金が9月1日がなくなった後も、フルタイムで作業できるようにしたいと思ったりなど...。

現在、私たちは私が着手できるように金を工面するのにcrowdfundingを使用する方法を調べています。 とってもすぐにブログ記事のポストを期待していてください。
しかし、これはcrowdfundingするうってつけの候補者に似ています。 --GILを無くすために10ユーロを支払うことをいとわない何千人ものあなた方が一応は存在します。今、私たちはこれを実現するだけです。

(原文:Posted by Armin Rigo)

2011年6月25日土曜日

調査の報告

原文はこちら: Report back from our survey

やぁ、みんな。

私は調査の結果を報告します。
まず最初に、私たちは多くのユーザーがPyPyを快くプロダクトで使うというレポートにとっても喜んでいます。
回答者の多く(97%)はPyPyは速いという理由で使用しています。
しかし、26%(回答者は複数回答を選択できる)で、メモリ使用量が少ないという理由でPyPyを使用しています。
PyPy使用していないユーザーのうち、最も一般的な理由は、"その他"に続きC拡張でした。

みんなからのコメントを読んでから私たちが学んだことがいくつかあります:
  1. Google docsはこのような物よりよいUIを必要としています。

  2. とても多くの人々がNumPyとSciPyを望み、C拡張を望んでいます。(25%の回答者はなんとかNumPyを使いたいと言った。)
    私たちは既にNumPyに関するプランを話題にした記事を書きました。

  3. 様々なOSのリポジトリにパッケージを持つことは、ユーザーの勉強と実行に大きな助けとなるでしょう。
すべての回答者の多大な貢献に感謝します!
最後に、もしあなたがPyPyをプロダクションで使っているのであれば、私たちはあなたに多いに感謝します。
そして、もし時間を割くことができるのであれば、メーリングリスト経由で連絡を下さい。

Thanks, Alex

(原文:Posted by Alex)