14

Rich Hickey らは、Clojure がinvokeDynamicJVM 7 または 8 で予定されている次のバージョンから大幅に改善されることはないと述べていますが、末尾再帰によるパフォーマンスの向上は見られるでしょう。

末尾再帰は影響しますか

(fn [...] (recur ...))

また

(loop [...] (recur ...))

コンパイラはおそらくすでにループ構造を生成しているので、これ以上速くなるとは思いません。

4

1 に答える 1

17

recur使用する場合、テール再帰関数があることをコンパイラーにすでに伝えているため、サンプルは速くなりません。これにより、コンパイラーはgoto(通常の命令ループのように)使用するバイトコードを生成できます。

もちろん、JVM がテール コールの最適化を取得すると、いくつかの利点があります。

もう recur を使用する必要はないので (使いたくない場合)、このような関数 (末尾再帰関数) を書くことができます。

(defn testfn [n] (when (not= 1000 n) (testfn n)))

現在、JVM は末尾再帰を検出できません。末尾呼び出しの最適化を追加することで、JVM は、これを記述したかのように上記の関数を認識できます (したがって、命令型のループ速度が得られます)。

(defn testfn [n] (when (not= 1000 n) (recur n)))

それほど大きな改善ではありませんが、テール コールの最適化が本当に優れている別のケースがあります。

相互に呼び出す関数 (場合によっては 2 つ以上) があり、スタックを保持する必要がない (末尾再帰的) 場合、JVM はそれらを最適化できます。recur他の関数にジャンプするように指示できないため、これは現在不可能です。ここに例があります。

(defn even [n] (if (zero? n) true (odd? (dec n)))
(defn odd  [n] (if (zero? n) false (even (dec n)))

大きな数でこれを試すと、スタックを吹き飛ばすことがわかっていますが、テールコールの最適化ではそうはなりません。

少し追加が残っています。trampolineすでにこれを実行できるようにする関数が呼び出されています (プログラミング スタイルが少し変更され、オーバーヘッドが多少あります)。説明する代わりにtrampoline、まさにそれを行うブログを紹介します。

http://pramode.net/clojure/2010/05/08/clojure-trampoline/

于 2010-11-29T18:47:34.020 に答える