Roslyn SyntaxNodesは再利用されますか?


124

私はRoslyn CTPを調査してきましたが、これはExpression Tree APIと同様の問題を解決しますが、どちらも不変ですが、Roslynはまったく異なる方法でそうします。

  • Expressionノードは親ノードへの参照がなく、を使用して変更ExpressionVisitorされます。そのため、大きなパーツを再利用できます。

  • SyntaxNode反対側のRoslyn は親への参照を持っているため、すべてのノードは事実上、再利用できないブロックになります。、などのメソッドはUpdateReplaceNode変更を加えるために提供されています。

これはどこで終わりますか?DocumentProjectISolution?APIは、ボタンの代わりにツリーの段階的な変更を促進しますが、各ステップは完全なコピーを作成しますか?

なぜ彼らはそのような選択をしたのですか?私が見逃している興味深いトリックはありますか?

回答:


181

更新:この質問は、2012年6月8日の私のブログの主題でした。すばらしい質問をありがとう!


すばらしい質問です。私たちはあなたが提起する問題について長い間議論しました。

次の特性を持つデータ構造が必要です。

  • 不変。
  • 木の形。
  • 子ノードから親ノードへの安価なアクセス。
  • ツリー内のノードからテキスト内の文字オフセットにマップできます。
  • 永続的

永続 I能力を意味し、ツリー内の既存のノードのほとんどを再利用し、編集をテキストバッファに行われたときに。ノードは不変なので、それらを再利用するための障壁はありません。これはパフォーマンスのために必要です。キーを押すたびにファイルの膨大な数のウォッジを再解析することはできません。編集の影響を受けたツリーの部分のみを再レクスおよび再解析する必要があります。

これら5つすべてを1つのデータ構造に入れようとすると、すぐに問題が発生します。

  • そもそもどうやってノードを作るのですか?親と子の両方が相互に参照し、不変なので、どちらが最初に構築されますか?
  • その問題をなんとか解決できたとしたら、どうすればそれを永続化できますか?別の親で子ノードを再利用することはできません。これは、新しい親があることを子に伝えることになるためです。しかし、子供は不変です。
  • その問題を解決できたとしましょう。新しい文字を編集バッファーに挿入すると、そのポイントの後の位置にマップされているすべてのノードの絶対位置が変化します。編集を行うとほとんどのノードのスパンが変更される可能性があるため、永続的なデータ構造を作成するのは非常に困難です。

しかし、Roslynチームでは、不可能なことを日常的に行っています。実際には、2つの解析ツリーを保持することで不可能を実現しています。「グリーン」ツリーは不変で永続的であり、親参照がなく、「ボトムアップ」で構築され、すべてのノードはその幅を追跡しますが、絶対位置は追跡しません。編集が発生すると、編集の影響を受けた緑のツリーの部分のみが再構築されます。これは通常、ツリー内の解析ノードの総数の約O(log n)です。

「赤」の木は、緑の木の周りに構築される不変のファサードです。オンデマンドで「トップダウン」構築され、編集ごとに破棄されます。親参照は、ツリーを上から下に降りていくときにオンデマンドで製造することで計算されます。また、下降すると、幅から絶対位置を計算して絶対位置を生成します。

ユーザーであるあなたには、赤い木しか見えません。緑の木は実装の詳細です。解析ノードの内部状態を覗いてみると、実際には別のタイプの別の解析ノードへの参照があることがわかります。これが緑のツリーノードです。

ちなみに、これらは「赤/緑の木」と呼ばれています。これらは、設計会議でデータ構造を描画するために使用したホワイトボードマーカーの色でした。色に他の意味はありません。

この戦略の利点は、不変性、永続性、親参照など、これらすべての優れた機能を利用できることです。コストは、このシステムが複雑であり、「赤い」ファサードが大きくなると、大量のメモリを消費する可能性があることです。現在、利益を失うことなくコストを削減できるかどうかを確認するための実験を行っています。


3
IProjectsとIDocumentsに関する質問の一部に対処するために、サービスレイヤーで同様のモデルを使用します。内部的には、構文ツリーの緑のノードと道徳的に同等な「DocumentState」および「ProjectState」タイプがあります。取得するIProject / IDocumentオブジェクトは、これらの赤いノードファサードです。デコンパイラーでのRoslyn.Services.Projectの実装を見ると、ほとんどすべての呼び出しが内部状態オブジェクトに転送されていることがわかります。
Jason Malinowski、2012年

@Eric発言して申し訳ありませんが、あなたは自分と矛盾しています。The expense and difficulty of building a complex persistent data structure doesn't pay for itself.ref:stackoverflow.com/questions/6742923/…高いパフォーマンス目標がある場合、そもそもなぜそれを不変にしたのですか?明白な理由とは別に他の理由しかありませんか?などについての理由に、スレッドセーフを作ることなどが容易
ルカシュマドン

2
@lukasその引用を文脈から外しています。前の文は、「。NETプログラムで文字列に対して通常行われる操作を見ると、まったく新しい文字列を作成することは、関連するすべての点でまったく悪くないため」でした。OTOH、通常、式ツリーで行われる操作(ソースファイルに数文字入力するなど)を見ると、完全に新しい式ツリーを構築するのは非常に悪いことです。したがって、彼らはその半分しか構築しません。
Timbo

1
@lukas私の推測:Roslynはバックグラウンドスレッドで動作するはずなので、不変性により、ユーザーがキーを押したときに変更されることを心配することなく、複数のスレッドが同じソースコードを同時に分析できます。ユーザー入力に応じて、実行中の分析タスクを停止することなく、不変ツリーを更新できます。したがって、不変性の主な目標は、Roslynを簡単に記述できるようにすること(そしておそらくクライアントが使いやすくすること)だと思います。
Qwertie

3
@lukas永続的なデータ構造は、通常、データ構造が変更よりもはるかに大きい場合、コピーよりも効率的です。あなたのポイントがあれば、私にはわかりません。
Qwertie
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.