JVMは末尾呼び出しの最適化を妨げますか?


99

私はこの質問でこの引用を見ました:Webサービスを構築するための優れた関数型言語は何ですか?

特にScalaは、自己再帰関数を除き、末尾呼び出しの除去をサポートしていません。これにより、実行できる構成の種類が制限されます(これは、JVMの基本的な制限です)。

これは本当ですか?もしそうなら、この基本的な制限を作成するのはJVMについて何ですか?

回答:


74

この投稿:再帰か反復か?役立つかもしれません。

つまり、セキュリティモデルとスタックトレースを常に利用できるようにする必要があるため、JVMでテールコールの最適化を行うのは困難です。これらの要件は理論的にはサポートできますが、おそらく新しいバイトコードが必要になります(John Roseの非公式提案を参照)。

Sunバグ#4726340にも詳細な説明があり、評価(2002年から)が終了します。

それでもそれは可能だと思いますが、それは小さな仕事ではありません。

現在、Da Vinci Machineプロジェクトでいくつかの作業が行われています。末尾呼び出しサブプロジェクトのステータスは「proto 80%」と表示されます。Java 7になる可能性は低いですが、Java 8での可能性は非常に高いと思います。


私は説明に完全に従わなかった。テールコールの最適化はコンパイラーによって実装されていると思いました。コンパイラーによって末尾呼び出しで最適化できる関数があると仮定すると、ループを使用して同じ機能を実装する同等の非再帰関数を持つこともできますよね?もしそうなら、これはコンパイラによって行うことができませんでした。JVMへの依存を追跡できません。これは、ネイティブのi386コードを生成したSchemeコンパイラとどのように違いますか?
Gautham Ganapathy

4
@Gautham:デバッグに関する私の声明は、JVMでの末尾呼び出しの排除の欠如に対する回避策としてトランポリンを使用することに関するものでした。テールコールの除去はJVMで実装でき、実装されているため(Arnold SchaighoferはOpenJDKで実装されており、LLVMでも実装されています)、それが可能かどうかは問題ありません。もちろん、MicrosoftのCLRは10年間テールコールの排除をサポートしており、F#のリリースはそれがゲームチェンジャーであることを実証しました。その答えは、JVMが停滞して以来ずっと続いていることだと思います。
JD

3
これはよくある誤解であり、よく繰り返される言い訳ですが、正しくありません。スタックインスペクション(および有用なスタックトレースの提供)によるセキュリティは、適切なテールコールと互換性がないことが長年にわたって確立されています。たとえば、2004年のこのペーパーを参照してください。citeseerx.ist.psu.edu / viewdoc /…回答が間違っているため、反対票を投じます。
ジャスティンシーヒー

5
@JustinSheehy:何が間違っていますか?問題は、「JVMは末尾呼び出しの最適化を妨げるのですか?」ということでした。そして答えは「いいえ、しかしそれは難しい」です。
マイケル・マイヤーズ

5
java8に含まれているかどうか知っていますか?
nachokk 2014

27

基本的な制限は、JVMがそのバイトコードでテールコールを提供しないことであり、その結果、JVM上に構築された言語がテールコール自体を提供する直接的な方法はありません。同様の効果(トランポリンなど)を実現できる回避策はありますが、パフォーマンスが著しく低下し、生成された中間コードが難読化されてデバッガーが役に立たなくなります。

したがって、SunがJVM自体にテール呼び出しを実装するまで、JVMは本番品質の関数型プログラミング言語をサポートできません。彼らは何年もの間それについて議論してきましたが、私は彼らがテールコールを実装することはないだろうと思います:彼らはそのような基本的な機能を実装する前にVMを時期尚早に最適化しており、Sunの努力は関数型言語ではなく動的言語に重点を置いているため非常に困難です。

したがって、Scalaは実際の関数型プログラミング言語ではないという非常に強力な議論があります。これらの言語は、30年以上前にSchemeが初めて導入されて以来、末尾呼び出しを必須の機能と見なしています。


5
Hence there is a very strong argument that Scala is not a real functional programming language -議論は実際には非常に弱いです。確かにありtail calls [as] an essential feature、基盤となるハードウェア(または仮想マシン)が直接サポートしている場合は便利です。しかし、それは実装の詳細です。
インゴ

8
@Ingo:実行時のユーザーのプログラムでのスタックオーバーフローが重大な問題であると見なさない場合のみ。バグトラッカーによると、Scalaコンパイラ自体でさえスタックオーバーフローに悩まされています。そのため、最も熟練したScala開発者でさえ、まだ間違い
JD

7
たとえばF#の擁護者になってもかまいません。しかし、F#以外のすべてのものに対して敵対的であると長い間(usenetでの数年前にも)あなたに言及しましたが、あなたの詳細は、あなたが何について話しているのか分からないことを示しています。ここのように:あなたの議論は、スタックオーバーフローで中止するプログラムを書くことができる言語は機能的なものではないようです?しかし、ヒープオーバーフローを引き起こす可能性のある言語に対して同じ議論をすることはできませんか?したがって、神聖なF#自体は機能的とは見なされません。
Ingo

7
@Ingo:関数型プログラミングのいくつかのイディオム(相互再帰や継続渡しスタイルなど)が機能するためには、末尾呼び出しの削除が必要になる場合があります。これがないと、プログラムはスタックオーバーフローになります。言語が慣用的な関数コードを確実に実行できない場合、それは関数ですか?答えはあなたが言うように判断の呼びかけですが、実際には重要な違いです。:マーティンTrojerはちょうどこのことについての興味深いブログ記事に公開martinsprogrammingblog.blogspot.com/2011/11/...
JD

2
それでも、JVM(残念ながら質問はありません)が末尾呼び出しを実行できないからといって、これは末尾呼び出しの削除が不可能であることを意味しません。これは、浮動小数点の計算はFPUを搭載したコンピューターでのみ可能であると述べたようなものです。
Ingo

22

Scala 2.7.xは、最終メソッドとローカル関数の自己再帰(それ自体を呼び出す関数)の末尾呼び出しの最適化をサポートしています。

Scala 2.8には、トランポリンのライブラリサポートも含まれている可能性があります。これは、相互再帰関数を最適化するための手法です。

Scala再帰の状態に関する多くの情報は、Rich Doughertyのブログにあります。


現在のScalaステータスに関する質問を更新していただけますか?
om-nom-nom 2013

@ om-nom-nom AFAIK、Scala側でもJVM側でも何も変更されていません。
ダニエルC.ソブラル2013


0

すべてのソースは、末尾再帰の場合にJVMが最適化できないことを示していますが、Javaパフォーマンスチューニング(2003、O'reilly)を読んだとき、著者は末尾再帰を実装することで再帰パフォーマンスを向上できると主張しました。

彼の主張は212ページで見つけることができます(「末尾再帰」を検索すると、2番目の結果になります)。何ができますか?


IBMは、JVM実装で何らかの形のTCOをサポートしています(最適化のため、保証はありません)。おそらく、Javaパフォーマンスチューニングの作成者は、この機能は最終的にすべてのJVMによって実装されると考えていました。ibm.com/developerworks/java/library/j-diag8.html
llemieng
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.