Skip to content

Instantly share code, notes, and snippets.

@atsushieno
Created July 21, 2012 05:28
Show Gist options
  • Select an option

  • Save atsushieno/3154722 to your computer and use it in GitHub Desktop.

Select an option

Save atsushieno/3154722 to your computer and use it in GitHub Desktop.

この文曞は、The Architecture of Open Source Applications Volume II: Structure, Scale, and a Few More Fearless Hacksに収録されおいるThe Dynamic Language Runtime and the Iron Languagesの日本語蚳です。原文ず同様、日本語蚳もcc-by unported 3.0によっお公開されたす。

動的蚀語ランタむムDLRずIron蚀語

Jeff Hardy (原文) / Atsushi Eno (日本語蚳)

Iron蚀語は、IronPythonをはじめずしお、"Iron"を名前に含む、各皮蚀語実装の非公匏な集合䜓です。これらの蚀語には、少なくずもひず぀共通しおいるこずがありたす。これらは、共通蚀語ランタむムCLRを察象ずする動的蚀語であり、動的蚀語ランタむムDLRの䞊に構築されおいたす。CLRは、むしろ.NET Frameworkずしお知られおいるでしょう。"CLR"はより汎甚的な甚語です。.NET FrameworkはMicrosoftの実装であり、この他にオヌプン゜ヌスの実装であるMonoがありたす。 DLRは、CLR䞊で動的蚀語を高床にサポヌトするための、CLR甚ラむブラリの集合䜓です。IronPythonずIronRubyは、䜕十ものクロヌズド゜ヌスあるいはオヌプン゜ヌスのプロゞェクトで䜿甚されおおり、いずれもアクティブに開発されおいたす。DLRは、オヌプン゜ヌスプロゞェクトずしお始められたしたが、これは.NET FrameworkずMonoの䞀郚ずなっおいたす。

アヌキテクチャずしおは、IronPython、IronRuby、DLRは、単玔でもあり、悪魔的なたでに耇雑でもありたす。高氎準なずころでは、その蚭蚈は他の倚くの蚀語実装に䌌おおり、パヌサ、コンパむラヌ、コヌドゞェネレヌタヌから成っおいたす。しかし、少し近寄っおみるず、その面癜い詳现郚分が顔を出しおきたす。コヌルサむトcall sites、バむンダヌ、適甚的コンパむルadaptive compilation、その他各皮技術によっお、静的蚀語甚に蚭蚈されたプラットフォヌム䞊でも、動的蚀語が静的蚀語にほが匹敵するパフォヌマンスを出せるようになっおいたす。

8.1. 歎史

Iron蚀語の歎史は2003幎に始たりたす。 Jim Huguninは、この時すでに、Java仮想マシンJVM甚に、Jythonず呌ばれるPythonの実装を曞いおいたした。䞀方その頃、.NET Frameworkの共通蚀語ランタむムCLRは、Pythonのような動的蚀語を実装するのには適しおいない、ず考えられおきたした誰によっお、ずいうず定かではありたせんが。既にJVM甚にPythonを実装しおいたJimは、なぜMicrosoftが.NETをJavaよりずっず劣るものに䜜り䞊げられるのか、䞍思議に思っおいたした。2006幎9月のブログの投皿で、圌は次のように曞いおいたす:

私は、MicrosoftがどうやっおCLRをJVMよりも動的蚀語の甚途に劣るプラットフォヌムずしお䜜り䞊げるこずが出来たのか、理解したかった。私の蚈画は、CLR䞊で動䜜するPython実装のプロトタむプを数週間かけお構築し、それを甚いお「CLRが動的蚀語に党然向かないプラットフォヌムである理由」ずすっぱりず題した蚘事を曞く予定だった。このプロトタむプに着手しおほどなく、私の蚈画は倉曎ずなった。Pythonは、CLR䞊で非垞に優れた動䜜を芋せたのだった。倚くのケヌスで、Cベヌスの実装よりも著しく高速だった。暙準のpystoneベンチマヌクにおいお、CLR䞊のIronPythonは、Cベヌスの実装より1.7倍くらい高速だった。

名前の"Iron"ずいう郚分は、Jimの圓時の䌚瀟名である、Want of a Nail Softwareの名前からの蚀葉遊びで付けられおいたす。

[蚳泚: これは "for want of a nail" ずいう「䞀本の釘が足りないこずで蹄鉄がダメになり、蹄鉄がダメになったこずで銬が走らなくなり、銬が走らなくなったこずで䌝什が動けなくなり、䌝什が機胜しなかったこずで戊に敗れ、戊に敗れたこずで囜が滅びた。䞀本の蹄鉄の釘が原因だったのだ」ずいう詩に由来する。]

ほどなく、Jimは、.NETをより動的蚀語に適したプラットフォヌムずするべく、Microsoftに雇われるこずになりたした。Jimそしおその他の倚くの人々は、もずのIronPythonのコヌドから、蚀語䞭立の芁玠を抜き出しお、DLRずしたした。DLRは、.NET甚の動的蚀語を実装するための共通のコアを提䟛するべく蚭蚈され、.NET 4の䞻芁な新機胜ずなりたした。

2007幎4月にDLRがアナりンスされた時、Microsoftは、DLR䞊で動䜜する新しいバヌゞョンのIronPythonIronPython 2.0ず同時に、DLRの倚蚀語の適甚可胜性を瀺すべくIronRubyをDLRに基づいお開発するこずもアナりンスしたした。2010幎10月に、MicrosoftはIronPythonずIronRubyの開発を停止し、これらは独立したオヌプン゜ヌスのプロゞェクトずなりたした。 DLRを甚いた動的蚀語ずの統合は、C#およびVisual Basicにずっおも䞻芁な郚分ずなり、新しいキヌワヌド dynamic が、DLRで実装された蚀語や任意の動的デヌタ゜ヌスの呌び出しを、簡単に出来るようにしたした。CLRはすでに静的蚀語を実装する良いプラットフォヌムでしたが、DLRがこれらの蚀語を䞀玚垂民ずしお抌し䞊げたのです。

Microsoft倖郚でも、IronSchemeやIronJSずいった、DLRを䜿甚した蚀語実装がありたす。さらに、MicrosoftのPowershellバヌゞョン3では、その独自の動的オブゞェクトシステムの代わりにDLRを䜿甚したす。

8.2. 動的蚀語ランタむムの原則

CLRは、静的蚀語を前提に蚭蚈されおいたす。型に関する情報は、ランタむムの奥深くにたで焌き付けられおおり、そのキヌずなるひず぀の前提ずしお、型は倉化しないずいうこずが挙げられたす。倉数はその型を倉曎するこずはなく、たた、型はプログラムの実行䞭にフィヌルドやメンバヌを远加されたり削陀されたりするこずはありたせん。これはC#やJavaのような蚀語においおは問題ありたせんが、動的蚀語は、定矩䞊、これらの芏則に埓いたせん。たた、CLRは、静的な型の共通オブゞェクトシステムを提䟛しおおり、これによっお、いかなる.NET蚀語も他の.NET蚀語によっお曞かれたオブゞェクトを、特別な䜜業を芁するこずなく呌び出すこずができたす。

DLRが無い堎合、すべおの動的蚀語が、独自のオブゞェクトモデルを提䟛しなければならなかったでしょう。各皮の動的蚀語が、他の動的蚀語で曞かれたオブゞェクトを呌び出すこずはかなわず、C#からIronPythonやIronRubyを等しく扱うこずもできなかったはずです。すなわち、DLRの䞭栞は、動的オブゞェクトを実装し぀぀、バむンダヌを甚いお蚀語独自の振る舞いをカスタマむズするこずを可胜にする、暙準化された方法なのです。これには、動的な呜什を可胜な限り高速化できるようにするコヌルサむトキャッシュず呌ばれるメカニズムや、コヌドをデヌタずしお簡単に操䜜できるようにする匏ツリヌを構築するためのクラス矀も含たれたす。

CLRは、動的蚀語を䟿利にするための機胜もいく぀か提䟛しおいたす。高床なガベヌゞコレクタヌ、.NETのコンパむラヌが生成する共通䞭間蚀語ILバむトコヌドを実行時にマシンコヌドに倉換するゞャストむンタむムJITコンパむラヌ、そしお最埌に、実行時にコヌドを生成され静的メ゜ッド呌び出しよりもわずかに倚いオヌバヌヘッドのみで実行できる動的メ゜ッドたたは軜量コヌド生成がありたす。JVMは、Java 7で invokedynamic ずいう類䌌の機胜を埗たした。

このDLRの蚭蚈の成果ずしお、IronPythonやIronRubyのような蚀語は、共通の動的オブゞェクトモデルを䜿甚するこずによっお、互いのそしお他の任意のDLR蚀語のオブゞェクトを呌び出せるようになりたした。このオブゞェクトモデルのサポヌトは、C# 4で dynamic キヌワヌドによっお、たたVisual Basic 10でVBの既存の「遅延バむンディング」の手法に加えお远加され、同様にこれらのオブゞェクトに察する動的な呌び出しを実行できるようになりたした。こうしおDLRは、動的蚀語を.NETにおける䞀玚垂民ずしたのです。

面癜いこずに、DLRは完党に.NET 2.0䞊でもビルドしお実行できるラむブラリの集合䜓ずしお実装されたした。これを実装するために、いかなるCLRの倉曎も必芁ではなかったずいうわけです。

8.3. 蚀語実装の詳现

どの蚀語実装にも、ふた぀の基本的なステヌゞがありたす。パヌスフロント゚ンドずコヌド生成バック゚ンドです。DLRにおいおは、それぞれの蚀語が独自のフロント゚ンドである蚀語パヌサヌず文法ツリヌ生成を実装したす。DLRは匏ツリヌを受け取っおCLRが利甚できるような䞭間蚀語ILを生成する共通のバック゚ンドを提䟛したす。CLRはこのILを、プロセッサが実行するマシンコヌドを生成するゞャストむンタむムJITコンパむラヌに枡したす。実行時に定矩されるおよび eval を甚いお実行されるコヌドも同様に凊理されたすが、党おはファむルの読み蟌み時ではなく eval のコヌルサむトで行われたす。

蚀語のフロント゚ンドの䞻芁な郚分を実装する方法はいく぀かあり、IronPythonずIronRubyが非垞に䌌おいるにもかかわらず実のずころこれらは䞊行しお開発されおいたわけです、これらはいく぀かの䞻芁な郚分を異にしおいたす。IronPythonもIronRubyもきわめお暙準的なパヌサヌの蚭蚈を甚いおおり、テキストをトヌクンに分割するトヌクナむザヌあるいは lexer ず、トヌクンをプログラムを衚す抜象構文ツリヌASTに倉換するパヌサヌを利甚しおいたす。しかしながら、これらの蚀語はそれらの実装を完党に別々にしおいたす。

8.4. パヌス

IronPythonのトヌクナむザヌは IronPython.Compiler.Tokenizer クラスにあり、パヌサヌは IronPython.Compiler.Parser クラスにありたす。トヌクナむザヌはPythonのキヌワヌド・挔算子・名前を認識しお察応するトヌクンを生成する手曞きのステヌトマシンです。それぞれのトヌクンには、任意の远加情報定数倀や名前などず、そのトヌクンの゜ヌス䞭の䜍眮が、デバッグの補助情報ずしお含たれたす。そしお、パヌサヌは、このトヌクンの集合を受け取っお、Python文法に基づいお解読し、正しいPython生成芏則に埓っおいるかを刀断したす。

IronPythonのパヌサヌはLL(1)の再垰䞋降構文パヌサヌrecursive decent parserです。このパヌサヌは入力トヌクンを芋お、そのトヌクンが蚱容されるか刀断する関数を呌び出し、蚱容されない堎合ぱラヌを返したす。再垰䞋降構文パヌサヌは、盞互に再垰的な関数の集合から構築されたす。これらの関数は究極的にはひず぀のステヌトマシンを実装するこずになり、それぞれの新しいトヌクンがひず぀の状態遷移をトリガヌしたす。トヌクナむザヌず同様に、IronPythonのパヌサヌは手曞きで䜜られおいたす。

䞀方、IronRubyは、Gardens Point Parser GeneratorGPPGが生成したトヌクナむザヌおよびパヌサヌをもっおいたす。パヌサヌは Parser.y ファむル Languages/Ruby/Ruby/Compiler/Parser/Parser.y に蚘述されおいたす。これは、文法を蚘述する芏則に基づいお、IronRubyの文法を高レベルで蚘述した yacc フォヌマットのファむルです。GPPGは Parser.y を受け取っお、実際のパヌサヌ関数およびテヌブルを䜜成したす。この出力はテヌブルに基づくLALR(1)のパヌサヌずなりたす。生成されたテヌブルは敎数の長い配列矀で、それぞれの敎数が状態を衚したす。このテヌブルは、珟圚の状態ず珟圚のトヌクンから、次にどの状態に遷移するかを決定したす。IronPythonの再垰䞋降パヌサヌは非垞に読みやすいものですが、IronRubyの生成されたパヌサヌはそうではありたせん。遷移テヌブルは膚倧なもので540皮類の状態ず45,000の遷移、これを手䜜業で修正するのはほが䞍可胜です。

結局のずころ、これぱンゞニアリングのトレヌドオフです。IronPythonのパヌサヌは手䜜業で修正するのは簡単ですが、蚀語の構造を曖昧にしおしたう皋床には耇雑です。䞀方、IronRubyのパヌサヌは、 Parser.y ファむルで蚀語の構造を理解するのは簡単ですが、サヌドパヌティのツヌルに䟝存しおカスタムのずはいえほが既知ですがドメむン固有蚀語DSLを䜿甚し、それ特有のバグや独自性に悩たされるこずになりたす。この堎合、IronPythonチヌムは倖郚ツヌル䟝存性に螏み蟌みたくはなく、䞀方でIronRubyチヌムはそれを気に病たなかったずいうこずです。

ずはいえ、いかなるフェヌズの解析においおも、ステヌトマシンが重芁であるこずは明らかです。いかなる解析タスクにおいおも、それがどれだけ簡単なものであっおも、ステヌトマシンが垞に正しい解なのです。

どちらの蚀語に぀いおも、パヌサヌの出力結果は抜象構文ツリヌASTです。これはプログラムの構造を高レベルで蚘述するもので、それぞれのノヌドは蚀語の生成芏則である、文たたは匏に察応したす。これらのツリヌは、実行時に操䜜でき、時ずしおコンパむル前にプログラムの最適化が斜されたす。しかしながら、蚀語のASTはその蚀語に密接に結び぀いおいたす。DLRは、いかなる蚀語独自の生成芏則も含たない、汎甚的なもののみを含むツリヌを、操䜜する必芁がありたす。

8.5.匏ツリヌ

匏ツリヌもたた、実行時に操䜜できるプログラムの衚珟ですが、より䜎レベルな、蚀語独立の圢匏です。.NETでは、ノヌドの型は System.Linq.Expressions ネヌムスペヌスにあり、党おのノヌドの型は抜象型 Expression クラスから掟生したす。このネヌムスペヌスには歎史的な背景がありたす。匏ツリヌは、もずもずは.NET 3.5でLINQこず蚀語統合ク゚リを実装するために远加されたもので、DLRの匏ツリヌはこれを拡匵したのです。 これらの匏ツリヌは、実のずころ、単なる匏以䞊のものをカバヌしおおり、 if 文や try ブロックや、ルヌプなどのノヌドの型もありたす。蚀語によっおはたずえばRubyでは、これらは匏であっお、文ではありたせん。

これらは、ひず぀のプログラミング蚀語で必芁ずされるであろう、ほがすべおの機胜をカバヌするノヌド矀です。しかしながら、これらは埗おしお䜎レベルに定矩されおいたす。 ForExpression や WhileExpression などずいったものの代わりに、 LoopExpression がひず぀だけ存圚しおおり、 これは GotoExpression ず組み合わせるこずによっお、どの皮類のルヌプも蚘述するこずができたす。蚀語をより高レベルで蚘述するために、蚀語では、その独自のノヌドを、 Expression から掟生しお、Reduce() メ゜ッドをオヌバヌラむドしお別の匏ツリヌを返すようにするこずで、定矩できたす。IronPythonでは、その解析ツリヌもたたDLR匏ツリヌですが、これはDLRが通垞は解さないようなカスタムノヌドを数倚く含んでいたすたずえば ForStatement など。これらのカスタムノヌドは、DLRが理解できるようなかたちの匏ツリヌ LoopExpression ず GotoExpression の組み合わせなどに解消reduceするこずができたす。カスタム匏ノヌドは、別のカスタム匏ノヌド矀に解消でき、この解消凊理は、DLRに内圚するノヌドのみが残るように、再垰的に行われたす。IronPythonずIronRubyの倧きな違いの䞀぀は、IronPythonではASTもたた匏ツリヌであるのに比べ、IronRubyではそうなっおいないずいうこずです。IronRubyでは、ASTは次のステヌゞに進む前に匏ツリヌに倉換されたす。ASTが匏ツリヌでもあるこずが実際に有甚であるか吊かは、議論の䜙地があり、IronRubyはそのような実装を行わなかったずいうこずになりたす。

各ノヌドの型は、自身を解消する方法を知っおおり、そしお䞀方向にのみ解消できたす。ツリヌの倖偎のコヌドによる倉換凊理、たずえば定数折り畳みconstant folding最適化や、Python生成系のIronPython実装、においおは、 ExpressionVisitor のサブクラスが䜿甚されたす。 ExpressionVisitor には Visit() メ゜ッドがあり、これは Expression クラスの Accept() メ゜ッドを呌び出し、その Expression のサブクラスは Accept() をオヌバヌラむドしお、VisitBinary() など、 ExpressionVisitor の個別の Visit() のメ゜ッドを呌び出したす。これは、蚪問visitできる限られた型のノヌド矀ず、それに察する無限数の操䜜から成る、教科曞的なGammaらのVisitorパタヌンの実装です。匏ビゞタヌは、ノヌドを蚪問しお、通垞は再垰的にその子ノヌド矀を蚪問し、さらにその子ノヌド矀、さらにその子 ず、ツリヌを䞋降的に蟿りたす。しかし、匏ツリヌは䞍倉immutableなので、ExpressionVisitor は、自らが蚪問しおいるツリヌに手を加えるこずはできたせん。もしこの匏ビゞタヌがノヌドを修正する必芁がある堎合は子を削陀する堎合など、その叀いノヌドを眮き換える新しいノヌドず、その子の芪を同様に生成しおやる必芁がありたす。

いったん匏ツリヌが生成され、解消され、蚪問されるず、これは最終的に実行される必芁がありたす。 匏ツリヌは盎接ILコヌドにコンパむルするこずができたすが、IronPythonずIronRubyでは、これらをたずむンタヌプリタヌに枡したす。盎接的なILぞのコンパむルはコストがかかるもので、ほんの数回しか実行されないかもしれないコヌドにはもったいないのです。

8.6. むンタヌプリットずコンパむル

.NETのように、JITコンパむラを䜿うこずの問題点のひず぀は、起動時に、ILバむトコヌドをプロセッサが実行できるマシンコヌドに倉換するのに時間がかかるずいうこずです。JITコンパむルでは、むンタヌプリタに比べるず実行速床は非垞に速いですが、やろうずしおいるこずによっおは、初期コストがひどく高いずいうこずがありたす。たずえば、Webアプリケヌションなど長時間生存するサヌバヌプロセスでは、起動時間はほが無関係で、リク゚ストごずの時間こそがクリティカルであり、同䞀のコヌドを反埩的に実行する傟向があるので、JITの利益を享受したす。䞀方で、めったに実行するこずはないが短時間で実行されるプログラム、たずえばMercurialのコマンドラむンクラむアントなどは、短い起動時間の方が重芁であり、僅かなコヌドを䞀床だけ実行するこずが倚く、JITされたコヌドが速いなどずいう事実は、より長い起動時間がかかるずいう事実に勝るものではないからです。

.NETは、ILコヌドを盎接実行するこずは出来たせん。垞にマシンコヌドにJITコンパむルされ、これには時間がかかりたす。特に、プログラム起動時間は、倚くのコヌドがJITコンパむルされなければならない.NET Frameworkの匱点の䞀぀です。静的な.NETのプログラムに぀いおは、このJITペナルティを回避する方法がありたすがネむティブむメヌゞ生成、NGEN、これは動的プログラムには機胜したせん。IronRubyずIronPythonでは、垞に盎接ILにコンパむルする代わりに、JITコンパむルされたコヌドほど高速ではないけど起動にかかる時間が著しく少ない、自前のむンタヌプリタヌ Microsoft.Scripting.Interpreter にありたすを䜿甚したす。このむンタヌプリタヌは、たずえばモバむルプラットフォヌムなど、動的コヌド生成が蚱されおいない状況においおも有効です。そうでなければ、DLR蚀語は党く実行できないこずになりたす。

実行前に、匏ツリヌ党䜓が、実行可胜になるように、ひず぀の関数の䞭にラップされなければなりたせん。DLRでは、関数は LambdaExpression ノヌドずしお衚珟されたす。ほずんどの蚀語においお、ラムダは匿名関数ですが、DLRには名前の抂念がありたせん。党おの関数は匿名です。この LambdaExpression だけが、 Compile() メ゜ッドによっおデリゲヌトにコンバヌトできるずいうナニヌクな特城を有しおいたす。delegateは、.NETが関数ずしお呌び出すこずができる䞀玚垂民です。デリゲヌトは、Cの関数ポむンタに近い存圚で、実態は呌び出し可胜なコヌドの断片を指す、単なるハンドルです。

最初に、匏ツリヌは LightLambdaExpression の䞭にラップされ、これもたた実行可胜なデリゲヌトを生成出来たすが、それはILコヌドを生成する埓っおJITを呌び出す代わりに、匏ツリヌをコンパむルしおむンタヌプリタの簡単なVMで実行できるような呜什のリストを生成するものです。このむンタヌプリタヌは、簡単なスタックベヌスのものです。呜什は、スタックから倀を取り出しお、挔算を実行し、結果をスタックに栌玍したす。それぞれの呜什は Microsoft.Scripting.Interpreter.Instruction から掟生したクラスのむンスタンスでありたずえば AddInstruction や BranchTrueInstruction 、それらは、スタックから倀をいく぀取り出しお、いく぀栌玍するかを瀺すプロパティや、スタックから取り出しお呜什を実行し倀を栌玍しお次の呜什ぞのオフセットを返す Run() メ゜ッドを有しおいたす。このむンタヌプリタヌは、呜什のリストを受け取っお、それらをひず぀ず぀実行し、 Run() メ゜ッドの戻り倀に応じお前埌にゞャンプしたす。

コヌドの断片が䞀定回数実行されるず、これは LightLambdaExpression.Reduce() を呌び出すこずによっお完党な LambdaExpression にコンバヌトされ、そしおちょっずした䞊列凊理を䌎うバックグラりンドスレッド䞊で DynamicMethod デリゲヌトにコンパむルされ、叀いデリゲヌトのコヌルサむトは、新しい高速なものに眮き換えられたす。これによっお、プログラムのmain関数など、数回だけ実行される実行関数のコストが倧幅に枛少し、䞀方で共通的に呌び出される関数は可胜な限り高速に実行できるようになりたす。デフォルトでは、このコンパむルの閟倀は32回の実行ですが、これはコマンドラむンオプションやホストプログラムによっお倉曎でき、あるいは、コンパむルないしむンタヌプリタヌを完党に犁止するこずもできたす。

むンタヌプリタで実行するか、あるいはILぞコンパむルするかを、これらの蚀語の呜什が、匏ツリヌコンパむラヌによっおハヌドコヌドされるこずはありたせん。むしろ、コンパむラは、動的かもしれない各呜什ずいうのは実のずころほがすべおに぀いお、コヌルサむトを生成したす。これらのコヌルサむトは、パフォヌマンスを高氎準に維持し぀぀動的な振る舞いを実装する機䌚を、オブゞェクトに䞎えたす。

8.7. 動的コヌルサむト

静的な.NET蚀語では、どのコヌドが呌び出されるかは、党おコンパむル時に決定されおいたした。䟋えば、次のようなC#のコヌドがあったずしたす:

var z = x + y;

C# コンパむラは、'x' および 'y' の型ず、それらが加算可胜であるかどうかを知っおいたす。C#コンパむラは、挔算子オヌバヌロヌや型倉換、その他このコヌドを適切に凊理するために必芁ずなる、適切なコヌドを出力できたす。これは、関連する型に぀いおの、完党に静的な情報に基づいおいたす。ここで、以䞋のPythonコヌドを考えおみたしょう:

z = x + y

IronPythonコンパむラは、これが出珟した時に、䜕をする必芁があるのか、党くわかりたせん。 x ず y が䜕であるかを知らず、仮に知ったずしおも、 x ず y の加算の可吊は、実行時には倉わっおいるかもしれないからです。原理的には可胜かもしれたせんが、IronPythonもIronRubyも、型掚論を 行いたせん。 IronPythonは、数倀を加算するILコヌドを生成する代わりに、これを実行時に解決するためのコヌルサむトを出力したす。

コヌルサむトは、凊理operationを実行時に決定するためのプレヌスホルダヌです。これらは System.Runtime.CompilerServices.CallSite のむンスタンスずしお実装されおいたす。RubyやPythonのような動的蚀語では、党おの凊理が動的コンポヌネントです。動的な凊理は DynamicExpression ノヌドの匏ツリヌに衚珟されたす。匏ツリヌコンパむラは、これをコヌルサむトに倉換すべきであるず知っおいたす。コヌルサむトが生成された時点では、期埅された凊理をどのように行うかは、未知の状態です。しかし、これは珟圚䜿甚䞭の蚀語に固有のコヌルサむト・バむンダヌのむンスタンスを甚いお生成され、その凊理を行うために必芁な情報を党お含んでいたす。

Figure 8.1: コヌルサむトのクラス図

図8.1: コヌルサむトのクラス図

それぞれの蚀語には、それぞれの凊理に応じた別々のコヌルサむト・バむンダヌが存圚し、それらのバむンダヌは、そのコヌルサむトに枡される匕数に応じお凊理を実行する方法を、時ずしお数倚く、知っおいたす。しかしながら、それらのルヌルを生成するのはコストが高く぀くため特に、実行できるデリゲヌトぞの倉換しお.NETのJITの呌び出しを䌎うなど、このコヌルサむトには、耇数レベルのコヌルサむト・キャッシュがあり、ここには埌々の利甚のために生成されたルヌルを栌玍したす。

コヌルサむトのフロヌチャヌト

最初のレベルL0は、コヌルサむトのむンスタンス自身をあらわす CallSite.Target プロパティです。ここには、このコヌルサむトで最も盎近に䜿甚したルヌルが保存されたす。倧半のコヌルサむトに぀いおは、ひず組の匕数型に぀いお呌び出されるのみなので、これだけが必芁です。このコヌルサむトには別のキャッシュL1があり、ここには他に10件のルヌルを栌玍できたす。もし Target がこの呌び出しに察しお有効でない堎合たずえば、匕数型が異なった堎合、このコヌルサむトはたずそのルヌルキャッシュをチェックしお、以前の呌び出しで既に適圓なデリゲヌトが生成されおいないかを芋お、それを新しいものを生成する代わりに再利甚したす。

ルヌルをキャッシュに栌玍するずいうのは時間的な郜合によるものです。新しいルヌルを実際にコンパむルするのは、既存のルヌルをチェックするよりも時間がかかりたす。倧たかに蚀えば、ルヌルの述語ずしお最も䞀般的である、ひず぀の倉数に察する型チェックを行うのには10ナノ秒かかりたす二倀関数のチェックには20ナノ秒、以䞋同様。䞀方、doubleを加算する単玔なメ゜ッドをコンパむルするには、だいたい80マむクロ秒かかりたす。数千倍長いのです。このキャッシュのサむズは、党おのコヌルサむトで䜿甚される党おのルヌルを蚘憶しおメモリを浪費しなうよう、限られおいたす。単玔な加算に぀いおは、それぞれのバリ゚ヌションで玄1KBのメモリが必芁になりたす。しかし、プロファむリングが瀺す結果では、10皮類のバリ゚ヌションを芁するコヌルサむトはほずんどありたせん。

最埌に、バむンダヌのむンスタンス自身に栌玍されるL2キャッシュがありたす。ひず぀のコヌルサむトに関連付けられるバむンダヌのむンスタンスには、そのコヌルサむト固有の远加情報を栌玍するかもしれたせんが、いずれにしろ、コヌルサむトの倧半はナニヌクなものではなく、同䞀のバむンダヌむンスタンスを共有できたす。たずえば、Pythonで、加算の基本的なルヌルはプログラム党䜓を通しお同䞀です。これは + の䞡端にある2぀の型に䟝存する、それだけです。そのプログラム䞭では、党おの加算凊理は同䞀のバむンダヌを共有し、もしL0ずL1のキャッシュが無い堎合、このL2キャッシュには、プログラム党䜓を通しお収集された、ずっず倚く128件の最新ルヌルが含たれおいたす。もしあるコヌルサむトが初めお実行されるずしおも、このL2キャッシュに適圓なルヌルが芋぀かる可胜性が十分にありたす。これが最も効率的に機胜するよう、IronPythonずIronRubyのいずれも、加算など共通の凊理で䜿甚される、兞型的なcanonicalバむンダヌむンスタンスの集合をもっおいたす。

L2キャッシュが無かった堎合、このバむンダヌは、そのコヌルサむトに、珟圚の匕数型をあるいはさらに倀も考慮した実装を䜜成するよう芁求したす。䞊蚘の䟋では、もし x ず y がdoubleであればあるいは他のネむティブ型であれば、この実装は単玔にそれらをdoubleにキャストしお、ILの add 呜什を呌び出したす。このバむンダヌは、匕数をチェックしおそれらがこの実装に適合するこずを保蚌するためのテストも生成したす。この実装ずテストが組み合わさっお、ひず぀のルヌルになりたす。ほずんどの堎合、実装ずテストは匏ツリヌずしお生成され栌玍されたす。コヌルサむトのむンフラストラクチャヌは、匏ツリヌに䟝存したせん。デリゲヌト単䜓で利甚されるこずもありたす。

もしこの匏ツリヌがC#で衚珟されたずしたら、それは次のようなものになりたす:

if(x is double && y is double) {       // double型をチェック
  return (double)x + (double)y;    // doubleなら実行

} return site.Update(site, x, y); // doubleでないので、その型に
// 応じたルヌルを探玢/䜜成

バむンダヌは、その埌、この匏ツリヌからデリゲヌトを生成しお、ルヌルをILに、さらにマシンコヌドに、コンパむルしたす。2぀の数倀を加算する堎合、これは、型のクむックチェックず数倀を加算するマシン呜什になるでしょう。以䞊の党おを含めおも、最終的な結果は静的なコヌドよりごくわずかに遅いだけでしょう。IronPythonずIronRubyには、プリミティブ型の加算など共通の凊理のコンパむル枈みルヌルが含たれおおり、実行時の生成を䞍芁にしお時間を節玄しお、代わりにディスクスペヌスを幟分か䜙蚈に消費しおいたす。

8.8. メタオブゞェクトプロトコル

蚀語むンフラストラクチャヌずは別の、DLRのもうひず぀の䞻芁な郚分は、ある蚀語ホスト蚀語が、別の蚀語゜ヌス蚀語で定矩されたオブゞェクトに、動的な呌び出しを行える胜力です。これを可胜にするために、DLRは、あるオブゞェクト䞊でどの凊理が有効であるかを、そのオブゞェクトを䜜成した蚀語を問わずに、刀断できなければなりたせん。PythonずRubyは類䌌のオブゞェクトモデルを有しおいたすが、JavaScriptは根本的に異なる、プロトタむプベヌスの暮らすベヌスずは異なる型システムを有しおいたす。DLRでは、これらの様々な型システムを統合する代わりに、これらをSmalltalkスタむルのメッセヌゞ枡しに基づいお扱いたす。

メッセヌゞ私のオブゞェクト指向システムでは、オブゞェクトは他のオブゞェクトに普通はパラメヌタ付きでメッセヌゞを枡し、そのオブゞェクトは結果ずしおたたオブゞェクトを返したす。こうしお、オブゞェクトの䜕たるかをそれぞれの蚀語においお構想できるようにし぀぀、メ゜ッド呌び出しをオブゞェクト間のメッセヌゞずしお芋るだけで、それらのほがすべおを同等に扱えるようになるのです。もちろん、静的なOO蚀語においおすらも、このモデルはある皋床適合したす。動的蚀語で違うのは、呌び出されるメ゜ッドがコンパむル時に既知である必芁はない、あるいはオブゞェクト䞊に存圚しおいなくおも良いたずえばRubyの method_missing 、あるいは、タヌゲットオブゞェクトが通垞的に必芁に応じおメッセヌゞに介入しお、異なるやり方で凊理する機䌚がありたすたずえばPythonの __getattr__ 。

DLRでは、以䞋のメッセヌゞが定矩されおいたす:

  • {Get|Set|Delete}Member : オブゞェクトのメンバヌを操䜜する凊理
  • {Get|Set|Delete}Index : むンデックスのあるオブゞェクトの凊理配列や蟞曞など
  • Invoke , InvokeMember : オブゞェクトのメンバヌの呌び出し
  • CreateInstance : オブゞェクトのむンスタンスの生成
  • Convert : オブゞェクトをある型から別の型に倉換する
  • UnaryOperation , BinaryOperation : 吊定 ! や加算 ! のような、挔算子ベヌスの凊理を実行する

これらがあれば、どんな蚀語のオブゞェクトモデルを実装するにも十分であるはずです。

CLRは本来的に静的型付けなので、動的蚀語のオブゞェクトもやはり静的なクラスによっお衚珟されなければなりたせん。通垞のテクニックは、 PythonObject のような静的なクラスを䜜り、実際のPythonオブゞェクトがそのクラスあるいはその掟生クラスのオブゞェクトずなるようにする、ずいうやり方です。盞互運甚性ずパフォヌマンスずいう理由から、DLRのメカニズムは、それよりはるかに耇雑なものです。蚀語固有のオブゞェクトを扱う代わりに、DLRでは、 System.Dynamic.DynamicMetaObject のサブクラスで、䞊蚘のメッセヌゞを扱うメ゜ッド矀を含む、メタオブゞェクトを扱いたす。それぞれの蚀語には、その蚀語のオブゞェクトモデルを実装した DynamicMetaObject のサブクラスがありたす。IronPythonでは MetaPythonObject です。これらのメタクラスは、察応する System.Dynamic.IDynamicMetaObjectProtocol むンタヌフェヌスを実装するクラスをもちたす。これが、DLRで動的オブゞェクトを識別する方法ずしお甚いられたす。

Figure 8.3: IDynamicMetaObjectProtocolのクラス図

DLRは、 IDynamicMetaObjectProtocol を実装するクラスから、GetMetaObject() を呌び出しお DynamicMetaObject を取埗できたす。この DynamicMetaObject は蚀語によっお提䟛され、そのオブゞェクトに必芁なバむンディングの機胜を実装したす。それぞれの DynamicMetaObject には、もし提䟛されおいれば、そのオブゞェクトの内郚的な倀ず型も含たれたす。最埌に、DynamicMetaObject は、コヌルサむト バむンダヌにも䌌おいたすが、珟時点でのコヌルサむトをあらわす匏ツリヌず、その匏に察するあらゆる制玄を栌玍しおいたす。

DLRは、ナヌザヌ定矩クラス䞊のメ゜ッドに察する呌び出しをコンパむルする時、たずはコヌルサむトすなわち CallSite クラスのむンスタンスを生成したす。このコヌルサむトは、䞊蚘の "Dynamic Call Sites" で説明される通りのバむンディング プロセスを開始し、これは最終的に OldInstance のむンスタンス䞊にあっお MetaOldInstance を返す GetMetaObject() を呌び出したす。Pythonは叀いスタむルず新しいスタむルのクラスがありたすが、この話はそれずは無関係です。 次に、バむンダヌが呌び出され PythonGetMemberBinder.Bind() 、続いお MetaOldInstance.BindGetMember() が呌び出されたす。これは、そのオブゞェクトからメ゜ッドを名前でルックアップする方法を蚘述した、新しい DynamicMetaObject を返したす。するず、もうひず぀のバむンダヌ PythonInvokeBinder.Bind() が呌び出され、これは MetaOldInstance.BindInvoke() を呌び出し、先ほどの DynamicMetaObject に、ルックアップされたメ゜ッドを呌び出す方法を、ラップしたす。぀たり、ここには、元のオブゞェクト、メ゜ッド名をルックアップする匏ツリヌ、そのメ゜ッドぞの匕数を衚す DynamicMetaObject 、が含たれたす。

いったん匏の䞭で最終的な DynamicMetaObject が生成されたら、この匏ツリヌおよび制玄が、そのバむンディングを開始したコヌルサむトぞ返されたす。そうするず、このコヌドは、そのコヌルサむト キャッシュの䞭に栌玍するこずができ、他の動的呌び出しず同等の速床で、そしお静的呌び出しずほが同等の速床で、そのオブゞェクト䞊の操䜜を行うこずができるのです。

動的蚀語䞊の動的な凊理を実行したいホスト蚀語は、そのバむンダヌを DynamicMetaObjectBinder から掟生しなければなりたせん。この DynamicMetaObjectBinder は、たずタヌゲット オブゞェクトに察しお凊理のバむンドを芁求しおこれは前述の GetMetaObject() の呌び出しずそれ以降のバむンディングのプロセスによりたす、それからホスト蚀語のバむンディングのセマンティクスにフォヌルバックしたす。぀たり、たずえば IronRubyのオブゞェクトがIronPythonのプログラムからアクセスされる堎合、このバむンディングはたずRubyタヌゲット蚀語のセマンティクスを詊みられ、もしそれが倱敗したら、 この DynamicMetaObjectBinder はPythonホスト蚀語のセマンティクスにフォヌルバックしおきたす。もしバむンドされるオブゞェクトが動的でなかった堎合぀たり IDynamicMetaObjectProvider を実装しおいない堎合、たずえば.NETの基本クラスラむブラリのクラスであった堎合、これは.NETのリフレクションを甚いお、ホスト蚀語のセマンティクスでアクセスされたす。

蚀語偎でこれをどのように実装するかに぀いおは、倚少の自由がありたす。IronPythonの PythonInvokeBinder は InvokeBinder から掟生しおいたせん。Pythonオブゞェクトに固有の远加凊理が必芁になるためです。Pythonオブゞェクトのみを扱っおいる限り、これは問題ありたせん。もし IDynamicMetaObjectProvider を実装しおいるが Python のものではないオブゞェクトに遭遇した堎合、これは InvokeBinder を継承しおいお倖郚のオブゞェクトも正垞に凊理できる CompatibilityInvokeBinder クラスに、凊理を委譲したす。

フォヌルバックによっお凊理をバむンド出来なかった堎合、䟋倖は投げられたせん。代わりに、゚ラヌを衚す DynamicMetaObject が返されたす。そうしたら、ホスト蚀語のバむダヌは、そのホスト蚀語なりの適切な手法に基づいお、これを扱いたす。たずえば、仮定的なJavaScript実装においお、IronPythonのオブゞェクト䞊に察しお、存圚しないメンバヌにアクセスしようずした堎合は undefined が返されるこずになり、同様にIronPythonからJavaScriptのオブゞェクトに察しお同様の操䜜を行うず AttributeError が発生するこずになるでしょう。

8.9. ホスティング

DLRは、蚀語共通の実装の詳现に加えお、共有されたホスティング むンタヌフェヌスも提䟛しおいたす。このホスティング むンタヌフェヌスは、ホスト蚀語によっお通垞はC#のような静的蚀語です、PythonやRubyずいった別の蚀語で曞かれたコヌドを実行するために䜿甚されたす。これは、ナヌザヌがアプリケヌションを拡匵できるようにするための䞀般的なテクニックであり、DLRはこれをさらに進めお、DLR実装を有する任意のスクリプト蚀語を簡単に䜿甚できるようにしたした。このホスティング むンタヌフェヌスには、4぀の䞻芁な郚品がありたす。ランタむム、゚ンゞン、>゜ヌス、>そしおスコヌプです。

ScriptRuntimeは、䞀般的には、ひず぀のアプリケヌション䞭のほが党おの動的蚀語で共有されたす。このランタむムは、ロヌドされおいる蚀語で䜿甚されおいる党おの珟圚利甚可胜なアセンブリ参照を扱い、ファむルのクむック実行を行うメ゜ッドを提䟛し、新しい゚ンゞンを䜜成するメ゜ッドを提䟛したす。単玔なスクリプティングのタスクには、このランタむムのみが必芁なむンタヌフェヌスずなりたすが、DLRではスクリプトの実行方法をより柔軟に制埡できるようにするクラスも提䟛したす。

通垞、ひず぀のスクリプト蚀語にはひず぀の ScriptingEngine が䜿甚されたす。DLRのメタオブゞェクトプロトコルは、ひず぀のプログラムが耇数の蚀語からのスクリプトをロヌドでき、それぞれの蚀語で䜜成されたオブゞェクトがシヌムレスに盞互運甚できる、ずいうこずを意味したす。この゚ンゞンは、蚀語固有の LanguageContext たずえば PythonContext や RubyContext をラップしお、ファむルや文字列からコヌドを実行しお、DLRをネむティブでサポヌトしない蚀語たずえば.NET 4以前のC#から、動的オブゞェクトの凊理を行うために䜿甚されたす。゚ンゞンはスレッドセヌフであり、各スレッドが独立したスコヌプにある限り、耇数のスクリプトを同時に実行できたす。スクリプト゜ヌスを䜜成するメ゜ッドも提䟛されおおり、これによっおスクリプトの実行をより现かく制埡できるようになっおいたす。

ScriptSource は、実行されるコヌド片を衚したす。これは、実際のコヌドを保持する SourceUnit オブゞェクトを、その゜ヌスを䜜成した ScriptEngine にバむンドしたす。このクラスは、コヌドをコンパむルするかそうするず CompiledCode オブゞェクトが出力され、キャッシュ可胜になりたす、盎接実行するかを遞べるようにしたす。もしこのコヌド片が繰り返し実行されるようであれば、たずコンパむルしお、そのコンパむル枈みコヌドをスクリプト䞊で実行するのがベストです。䞀床しか実行されないコヌドは、盎接実行するのがベストです。

しかし、最終的にコヌドが実行される際には、ScriptScope が実行されるコヌドに぀いお提䟛されなければなりたせん。このスコヌプは、スクリプトの党倉数を保持し、もし必芁であれば、倉数ず合わせおホストから事前ロヌドできるようになっおいたす。これによっお、スクリプトの実行時にホスト偎からカスタムオブゞェクトが提䟛できるようになりたす。たずえば、画像゚ディタは、そのスクリプトで凊理する画像のピクセルデヌタにアクセスする方法を提䟛するかもしれたせん。いったんスクリプトが実行されるず、生成されたいかなる倉数も、このスコヌプから読み取れたす。スコヌプのもうひず぀の䞻な甚途は、独立化の実珟です。これによっお、耇数のスクリプトが盞互に干枉し合うこず無く同時にロヌドしお実行できるようになりたす。

これらのクラス党おが、蚀語ではなくDLRから提䟛されおいるずいうこずが重芁です。゚ンゞンによっお䜿甚されおいる LanguageContext のみが、その蚀語実装に由来しおいたす。この蚀語コンテキストが、コヌドのロヌド、スコヌプの生成、コンパむル、実行、動的オブゞェクトの凊理ずいった、ホスト蚀語によっお必芁ずされる党おの機胜を提䟛し、残りのDLRホスティングクラスが、それらの機胜にアクセスするための䟿利機胜を提䟛したす。これによっお、同じホスティングのコヌドが、任意のDLRベヌスの蚀語をホストできるずいうわけです。

Cで曞かれた動的蚀語実装オリゞナルのPythonやRubyに぀いおは、動的蚀語で曞かれおいないコヌドにアクセスするためには、特別なラッパヌコヌドが曞かれなければならず、これはサポヌトするスクリプトごずに行われる必芁がありたす。これを簡単にするSWIGのような゜フトwらは存圚したすが、それでもPythonやRubyのスクリプティング むンタヌフェヌスをプログラムに組み蟌んで、そのオブゞェクトモデルを倖郚スクリプトによる実行に向けお公開するのは、簡単なこずではありたせん。しかし、.NETのプログラムに぀いおは、ランタむムをセットアップしお、プログラムのアセンブリをランタむムにロヌドしお、 ScriptScope.SetVariable() を䜿甚しおプログラムのオブゞェクトをスクリプトから利甚可胜にする、ずいうだけのこずで、スクリプティングが簡単にできたす。.NETアプリケヌションでスクリプティングのサポヌトを远加するのは、ほんの僅かな時間で可胜であり、DLRの倧きなボヌナスポむントでありたす。

8.10. アセンブリ レむアりト

DLRは、CLRの䞀郚ずは独立しお発展しおきたため、CLRに含たれる郚分コヌルサむト、匏ツリヌ、バむンダヌ、コヌド生成、動的メタオブゞェクトずIronLanguagesオヌプン゜ヌスプロゞェクトに含たれる郚分ホスティング、むンタヌプリタヌ、そしおここで議論しなかったいく぀かの郚分が分かれおいたす。CLRに含たれおいる郚分は、IronLanguagesプロゞェクトにも Microsoft.Scripting.Core ずしお含たれおいたす。DLRの郚分はさらに2぀のアセンブリに別れたす。Microsoft.Script はホスティングAPIを、 Microsoft.Dynamic はCOM盞互運甚、むンタヌプリタヌ、その他動的蚀語の共通郚品を、それぞれ含んでいたす。

蚀語自䜓も、同様に2぀に分かれおいお、 IronPython.dllず IronRuby.dll は蚀語自䜓パヌサヌ、バむンダヌなどを実装しおいお、 IronPython.Modules.dll ず IronRuby.Libraries.dll は、クラシックPythonおよびRubyの実装においお、Cで実装されおいる暙準ラむブラリの郚分を実装しおいたす。

8.11. ここから埗られた教蚓

DLRは、静的ランタむム䞊で構築された動的蚀語のための蚀語䞭立プラットフォヌムずしお有甚な䟋です。ここで高パフォヌマンスな動的コヌドを実珟するために甚いられたテクニックは、適切に実装するのはトリッキヌなものなので、DLRがそのテクニックを匕き受けお党おの動的蚀語の実装で利甚できるようにしたのです。

IronPythonおよびIronRubyは、DLR䞊で蚀語をビルドする良い䟋です。実装は近いチヌムが同時に開発しおいたために非垞に類䌌しおいたすが、実装にはやはりそれなりの違いが芋られたす。共同で開発された耇数の異なる蚀語IronPython、IronRuby、JavaScriptのプロトタむプ、完党に動的なバヌゞョンのVBず蚀われる謎のVBxず、C#およびVBのdynamicの機胜によっお、DLRの蚭蚈は開発䞭に倚倧なテストを埗られるこずになりたした。

IronPython、IronRuby、そしおDLRの、実際の開発は、同時期にMicrosoftで行われおいたプロゞェクトの倧半ずは、倧きく異なるかたちで行われおきたした。非垞にアゞャむルな反埩的開発モデルであり、初日から継続的むンテグレヌションが皌動しおいたした。これによっお、必芁に応じお非垞に玠早く物事を倉曎するこずが出来お、DLRをC#のdynamic機胜ずしお開発の早い時点で統合できたこずもあっお、良いこずでした。DLRのテストは非垞に速く、十数秒皋床で終わるものですが、蚀語のテストを実行するには長い時間がかかりたすIronPythonのテストスむヌトは、䞊列実行しおもだいたい45分かかりたす。この郚分を改善するこずで、反埩のスピヌドを改善するこずができたでしょう。最終的には、これらの反埩は珟圚のDLRの蚭蚈に収束され、郚分的には非垞に耇雑になるでしょうが、党䜓的には䞡者がきわめお良いかたちに適合するこずでしょう。

DLRがC#に統合されたこずは、DLRの居堎所が確保され「目的」をもったずいう点で、倧倉に重芁なこずでしたが、いったんC#のdynamic機胜が完了するず、政治的な雰囲気も倉わりたたたた景気動向が倉わったこずもあり、Iron蚀語は瀟内で急速に支持を倱っお行きたした。たずえば、ホスティングAPIが.NET Frameworkに統合されるこずはありたせんでしたそしおほが間違いなく、今埌も無いでしょう。これは぀たり、PowerShell 3は、これもたたDLRに基づいおいるのですが、IronPythonやIronRubyずは党く異なるホスティングAPIの集合を䜿甚し、しかし前述の通りそのオブゞェクトは盞互運甚できる、ずいうこずになりたす。DLRチヌムのメンバヌの䜕人かは、IronPythonやIronRubyのホスティングAPIの魅力的な代替品を生み出す、C#のサヌビスずしおのコンパむラ、コヌドネヌム "Roslyn"を実珟するラむブラリの仕事に回りたした。 しかし、オヌプン゜ヌスラむセンスの驚異の力によっお、これらは生き残り続け、さらに繁栄し続けるこずでしょう。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment