動的に型付けされた言語のコンパイラーを作成する際の型付けに関連する課題は何ですか?


9

、この話、グイド・ヴァンロッサムは言ってそれにコメント、Pythonコードのコンパイラを書くための試みについて(27:30)に話しています。

すべての素晴らしい動的型付けプロパティを維持し、プログラムのセマンティックな正確性を維持するコンパイラーを書くのはそれほど簡単ではないことがわかりましたより速く

Pythonのような動的に型付けされた言語のコンパイラを書く際の型付けに関連する(可能な)課題は何ですか?


この場合、動的型付けはほとんど最大の問題ではありません。Pythonの場合は、動的スコープです。
SK-logic

他の人々が、プラットフォームに動的型付けを組み込むことがここでの正しい答えであると主張したことは注目に値します。マイクロソフトはまさにこの理由でDLRに多額の資金を投入しました。そして、NeXT / Appleは何十年もの間、その流れの中間にありました。これはCPythonには役立ちませんが、IronPythonは効果的に静的にPythonをコンパイルできることを証明し、PyPyはそうする必要がないことを証明します。
abarnert 2013年

2
@ SK-logic Pythonの動的スコープ?最後に確認したところ、この言語のすべての構成要素は字句スコープを使用しています。

1
@ SK-logicコードを動的に作成して実行できますが、そのコードもレキシカルスコープで実行されます。Pythonプログラムのすべての変数について、ASTを調べるだけで、変数がどのスコープに属しているかを簡単に判別できます。あなたはの思考かもしれexec声明 3.0以降消え、したがって外にある、私の対価(そしておそらくグイドさん、話は2012年からであるとして)。例を挙げていただけますか?「動的スコープ」の定義([私のスコープとは異なる](en.wikipedia.org/wiki/Dynamic_scoping)の場合)。

1
@ SK-logic私にとって実装の詳細である唯一のものは、へのlocals()呼び出し間で持続する戻り値の変更localsです。文書化されており、実装の詳細ではないことは、各変数がどのスコープで検索されるlocalsかさえもglobals変更できないことです。変数を1回使用するたびに、参照先のスコープが静的に決定されます。これにより、レキシカルにスコープが明確になります。(そして、ところで、evalそしてexecどちらかの間違い実装の詳細ではありません! -私の答えを見て)

回答:


16

あなたは質問をする際にグイドの発言を単純化しすぎました。問題は、動的に型付けされた言語用のコンパイラを作成することではありません。問題は、(基準1)常に正しいものを記述し、(基準2)動的型付けを維持し(基準3)は、大量のコードに対して著しく高速です。

Pythonの90%(失敗基準1)を実装し、一貫して高速にするのは簡単です。同様に、静的型付けを使用してより高速なPythonバリアントを作成することは簡単です(失敗基準2)。100%の実装も簡単です(複雑な言語を内部で実装するのは簡単です)が、これまでのところ、簡単に実装する方法はすべて比較的遅くなっています(失敗基準3)。

正しく、言語全体を実装し、一部のコードでは高速なインタープリターとJITを実装することは、かなり困難ですが(PyPyを参照)、JITコンパイラーの作成を自動化した場合にのみ可能です(Psycoはそれなしで実行しました) 、しかしそれがスピードアップできるコードは非常に限られていた)。ただし、静的について話しているので、これは明示的に範囲外であることに注意してください。(別名:事前)コンパイラ。これは、静的コンパイラーでそのアプローチが機能しない理由を説明するためだけに言及しています(または、少なくとも既存の反例はありません)。まず、プログラムを解釈して観察し、次にループの特定の反復(または別の線形コード)のコードを生成する必要があります。パス)、次に、その特定の反復のみに当てはまる(または少なくとも、すべての可能な反復に当てはまるわけではない)仮定に基づいて、地獄を最適化します。期待は、そのコードのその後の多くの実行も期待に一致し、最適化の恩恵を受けることです。正確さを保証するために、いくつかの(比較的安価な)チェックが追加されています。これをすべて行うには、何に特化するかというアイデアと、遅いが一般的な実装にフォールバックする必要があります。AOTコンパイラにはどちらもありません。彼らはまったく専門できない見えないコード(動的にロードされたコードなど)に基づいており、不注意に特殊化することは、より多くのコードを生成することを意味し、多くの問題(icacheの使用率、バイナリサイズ、コンパイル時間、追加のブランチ)があります。

言語全体正しく実装するAOTコンパイラーの実装も比較的簡単です。ランタイムを呼び出してインタープリターがこのコードを入力したときに行うことを行うコードを生成します。Nuitka(主に)がこれを行います。ただし、インタープリターと同じくらいの不必要な作業を行う必要があるため、これはパフォーマンスの利点をあまりもたらしません(失敗基準3)。コンパイルしたことを実行するCコードのブロックにバイトコードをディスパッチする必要はありません。しかし、これはかなり小さなコストです-既存のインタプリタで最適化する価値があるほど十分に重要ですが、独自の問題で新しい実装全体を正当化するほど重要ではありません。

3つの基準をすべて満たすには何が必要ですか?わかりません。Pythonプログラムから具体的な型、制御フローなどに関する情報を抽出できる静的分析スキームがいくつかあります。単一の基本ブロックの範囲を超えて正確なデータを生成するものは非常に遅く、プログラム全体、または少なくともそのほとんどを見る必要があります。それでも、おそらく組み込み型に対するいくつかの操作を最適化する以外に、その情報を使って多くを行うことはできません。

なんで?端的に言えば、コンパイラーは実行時にロードされたPythonコードを実行する機能(失敗基準1)を削除するか、Pythonコードによって無効化される可能性のある想定をまったく行いません。残念ながら、そのプログラムを最適化するために有用でほとんどすべて含まれていますに渡された関数を含むグローバルがリバウンドすることができが、クラスが変異または完全に置き換えられ、モジュールがあまりにも任意に変更することができ、インポートすることができますが、いくつかの方法でハイジャックすることができ、などA単一の文字列evalexec__import__または他の多数の機能、そのいずれかを行うことができます。つまり、大きな最適化をほとんど適用できず、パフォーマンス上の利点はほとんどありません(失敗基準3)。上記の段落に戻ります。


4

最も難しい問題は、いつでもすべてのタイプを把握することです。

CやJavaのような静的言語では、型宣言を見ると、そのオブジェクトが何であり、何ができるかがわかります。変数が宣言されている場合int、それは整数です。これは、たとえば、呼び出し可能な関数参照ではありません。

Pythonでは、そうすることができます。これは恐ろしいPythonですが、合法です:

i = 2
x = 3 + i

def prn(s):
    print(s)

i = prn
i(x)

さて、この例はかなり愚かですが、それは一般的な考えを示しています。

より現実的には、組み込み関数を、少し異なることを行うユーザー定義関数(呼び出し時に引数をログに記録するバージョンなど)に置き換えることができます。

PyPyは、コードが実際に何を実行するかを監視した後でジャストインタイムコンパイルを使用します。これにより、PyPyは処理を大幅に高速化します。PyPyはループを監視し、ループが実行されるたびに、変数fooが常に整数であることを確認できます。次に、PyPy fooはループを通過するたびにの型を検索するコードを最適化し、多くの場合、整数を表すPythonオブジェクトをfoo削除して、CPUのレジスタに存在する数値になるだけです。これは、PyPyがCPythonよりも高速になる方法です。CPythonは型の検索を可能な限り高速に実行しますが、検索を実行しなくても高速です。

詳細はわかりませんが、静的コンパイラテクノロジーを適用してPythonを高速化する(LLVMを使用する)Unladen Swallowというプロジェクトがあったことを覚えています。Unladen SwallowをGoogle検索して、なぜ期待どおりに機能しなかったのかについてのディスカッションが見つかるかどうかを確認することをお勧めします。


Unladen Swallowは、静的コンパイルや静的型についてではありませんでした。最終的には、CPythonインタープリターをすべての動的さを備えたLLVMに、ファンシーな新しいJIT(ParrotやDLR for .NET…やPyPyのようなもの)に移植するのが効果的でした。そうすることで、CPython内で多くのローカル最適化が見つかりました(その一部はメインライン3.xになりました)。Shedskinはおそらく、Pythonを静的にコンパイルするために静的型推論を使用したと考えているプロジェクトです(ただし、ネイティブコードに直接ではなく、C ++に対して)。
abarnert 2013年

Unladen Swallowの作者の1人であるReid KlecknerがUnladen Swallow Retrospectiveを投稿しました。これは、この文脈では一読の価値があるかもしれませんが、実際には技術的なものよりも管理とスポンサーシップの課題についてです。
abarnert 2013年

0

他の答えが言うように、重要な問題は型情報を理解することです。静的に実行できる範囲で、適切なコードを直接生成できます。

ただし、静的に実行できない場合でも、実際の型情報を取得すると、実行時だけで適切なコードを生成できます。この情報は、多くの場合、安定しているか、特定のコードポイントの特定のエンティティに対して最大でいくつかの異なる値を持つことが判明します。SELFプログラミング言語は、積極的なランタイム型の収集およびランタイムコード生成のアイデアの多くを開拓してきました。そのアイデアは、JavaやC#などの最新のJITベースのコンパイラで広く使用されています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.