1 ポイント 投稿者 GN⁺ 2024-08-20 | 1件のコメント | WhatsAppで共有
  • Tail Call: 関数が返る直前に行われる関数呼び出し。Tail Call最適化が発生すると jmp 命令が使われ、呼び出しスタックを削減する。
  • 利点:
    • スタックメモリ使用量を O(n) から O(1) に削減する。
    • 関数呼び出しの性能オーバーヘッドを取り除き、効率的な反復制御構造として利用できる。

インタプリタループの問題点

  • 問題点:
    • 関数が大きくなり制御フローが複雑になるほど、重要なデータをレジスタに保持しにくくなる。
    • 高速パスと低速パスが混在すると、コード品質が低下する。

Tail Callを活用したインタプリタループ改善

  • 解決策: Tail Callを使って各処理を小さな関数に分離し、各関数が次の処理をTail Callで呼び出す。
  • 利点:
    • レジスタ割り当てを制御できる。
    • 高速パスと低速パスを分離してコード品質を維持できる。
    • 独立した命令シーケンスを最適化できる。

限界

  • 非Tail Callが存在する場合の問題: 非Tail Callがあるとスタックフレームが生成され、データがスタックに保存されて性能が低下する。
  • 複雑な例外処理: 例外処理が複雑な場合、コードの重複と複雑さが増す。
  • 移植性の問題: musttail 属性は標準ではないため、すべてのコンパイラでサポートされているわけではない。

GN⁺の要約

  • Tail Call最適化は性能向上に重要な役割を果たし、特にProtobufパースで大きな成果を示す。
  • この技術は、Cで書かれた主要言語インタプリタ(Python、Ruby、PHP、Luaなど)にも適用できる。
  • musttail 属性の移植性の問題は解決すべき課題である。
  • 類似の機能を提供するプロジェクトとして、LuaJIT、wasm3 WebAssemblyインタプリタなどがある。

1件のコメント

 
GN⁺ 2024-08-20
Hacker Newsのコメント
  • C標準の提案書には、return goto (expression); という形式で末尾呼び出しが含まれている

    • [[musttail]] を標準化するよりも、ローカルオブジェクトの寿命が保証され、広範なエスケープ解析を必要としない
  • Rust愛好家向けには、become キーワードを追加する古いRFCがあった

    • 2018エディションの目標に集中するため延期されたが、最近また議論されている
    • 再び採用される可能性がある
  • C++でインタープリタを高速化する方法は、主に computed goto を使うことだ

    • 呼び出し規約の問題を回避できる
    • computed goto スタイルや末尾呼び出しスタイルを使うと、分岐予測器への負荷を減らせる
  • 末尾呼び出しを使ってコンテキストを切り替える問題では、呼び出し規約を使う関数が必要になる

    • 関数終了時に状態を復元するため、レジスタを無駄に消費する
    • luajit remake ブログで代替案と分析が提供されている
  • [[musttail]] 属性が GCC、Visual C++、その他の人気コンパイラにも広がることを期待している

    • [[musttail]] 属性は GCC に追加されつつある
  • C++対応に触れつつ、C++では末尾呼び出しがほとんどないと指摘している

    • たとえば、デストラクタを持つクラスのオブジェクトを返す場合は末尾呼び出しにならない
  • C++の [[musttail]] 関数で例外を投げたらどうなるのか気にしている

    • 例外スタックが完全に分離されるのかと質問している
  • 単純な例では、良いコード生成のために __attribute__((musttail)) は必要ないと述べている

    • エラー処理関数の呼び出し速度はそれほど気にしないだろう
    • 特定の構造により、信頼できるジャンプテーブルが生成される
  • トランポリンを使い、返された関数ポインタを外側のループで呼び出す方式の速度を気にしている

    • この方式には移植可能なCという利点がある
  • [[musttail]] でラップされた例外パスの例を明確にしてほしいという要望がある

    • [[musttail]] がスタックフレームの構築とレジスタスピルを防ぐ理由を説明している
    • 例外パスが実際に呼び出されたときにだけ、スタックフレームの構築とレジスタスピルが発生する
    • 例外パスはめったに呼び出されないため、性能への影響は大きくない
    • 分岐予測の効果により、追加作業の可能性が高速パスを遅くすることがある