web-dev-qa-db-ja.com

パフォーマンス:Javascriptでの再帰と反復

私は最近、Javascriptの機能的な側面と、SchemeとJavascript(後者)の関係についてのいくつかの記事(例 http://dailyjs.com/2012/09/14/functional-programming/ )を読みましたOOの側面はプロトタイピングベースの言語であるSelfから継承されたものですが、最初は関数型言語です。

ただし、私の質問はより具体的です。Javascriptでの反復と反復のパフォーマンスに関するメトリックスがあるかどうか疑問に思っていました。

インタプリタ/コンパイラが再帰を反復に変換するため、一部の言語では(設計上、反復の方が優れています)違いはごくわずかですが、少なくとも部分的には機能的であるため、おそらくこれはJavascriptの場合ではないようです言語。

24
mastazi

JavaScript 末尾再帰最適化を実行しません 、つまり、再帰が深すぎる場合、コールスタックオーバーフローが発生する可能性があります。反復にはそのような問題はありません。再帰が多すぎると思い、本当に再帰が必要な場合(フラッドフィルなど)、再帰を独自のスタックに置き換えます。

反復は単に関数の別のポイントにジャンプするのに対して、関数の呼び出しと戻りは状態の保存と復元を必要とするため、再帰のパフォーマンスはおそらく反復のパフォーマンスよりも劣ります。

28
Triang3l

更新:ES2015以降、 JavaScriptにTCOがある なので、以下の引数の一部はもはや有効ではありません。


Javascriptには末尾呼び出しの最適化はありませんが、多くの場合、再帰が最善の方法です。そして誠実に、Edgeの場合を除いて、コールスタックオーバーフローが発生することはありません。

パフォーマンスは覚えておくべきことですが、時期尚早の最適化も。再帰は反復よりもエレガントだと思うなら、それを試してください。これがあなたのボトルネックであることが判明した場合(それは決してないかもしれません)、あなたはいくつかの醜いイテレーションで置き換えることができます。しかし、ほとんどの場合、ボトルネックはコード自体ではなく、DOM操作またはより一般的にはI/Oにあります。

再帰は常によりエレガントです1

1:個人的な意見。

20

私はJavaScriptでのこのパフォーマンスにもかなり興味を持っていたので、(古いバージョンのノードで)いくつかの実験を行いました。階乗計算機を反復と対比して再帰的に記述し、ローカルで数回実行しました。結果は、税を持っている再帰にかなり歪んでいるように見えました(予想されました)。

コード: https://github.com/j03m/trickyQuestions/blob/master/factorial.js

Result:
j03m-MacBook-Air:trickyQuestions j03m$ node factorial.js 
Time:557
Time:126
j03m-MacBook-Air:trickyQuestions j03m$ node factorial.js 
Time:519
Time:120
j03m-MacBook-Air:trickyQuestions j03m$ node factorial.js 
Time:541
Time:123
j03m-MacBook-Air:trickyQuestions j03m$ node --version
v0.8.22
7
Dr.HappyPants

OPによる 要求に従って チップインします(自分を馬鹿にすることなく、できれば:P)

私たちは、再帰はコーディングのよりエレガントな方法にすぎないということに全員が同意していると思います。うまくやれば、より保守しやすいコードを作ることができます。これは、0.0001msを節約することと同じくらい重要です(それ以上ではないにしても)。

JSがTail-call最適化を実行しないという引数に関する限り、それはもはや完全に真実ではありません。 ECMA5の厳密モード を使用すると、TCOが有効になります。昔はあまり満足していませんでしたが、少なくとも今ではarguments.calleeは、strictモードでエラーをスローします。上記のリンクはバグレポートへのリンクですが、バグはWONTFIXに設定されています。さらに、標準のTCOが近づいています:ECMA6(2013年12月)。

直感的に、そしてJSの機能的な性質に固執して、私は再帰が99.99%の時間でより効率的なコーディングスタイルであると思います。しかし、フローリアン・マーゲインは、ボトルネックが他の場所で見つかる可能性が高いと彼が言ったときにポイントがあります。 DOMを操作している場合は、可能な限り保守可能なコードの記述に重点を置いていると思われます。 DOM APIとは、遅いということです。

私はどちらがより速いオプションであるかについて決定的な答えを提供することは不可能に近いと思います。最近、私が見た多くのjsprefは、ChromeのV8エンジンが一部のタスクで途方もなく高速であり、FFのSpiderMonkeyで4倍遅く実行され、その逆も同様であることを示しています。最新のJSエンジンには、コードを最適化するためのさまざまなトリックがあります。私は専門家ではありませんが、たとえば、V8はクロージャー(および再帰)用に高度に最適化されていると思いますが、MSのJScriptエンジンはそうではありません。 SpiderMonkeyは多くの場合、DOMが関係しているときにパフォーマンスが向上します...

つまり、JSでいつもそうであるように、どの技術がよりパフォーマンスが高くなるかは、予測するのは不可能に近いと言えます。

6

厳密モードがない場合、反復パフォーマンスは通常、わずかに再帰よりも高速です(さらに、JITにもっと仕事をさせる)。末尾再帰の最適化は、呼び出しシーケンス全体をジャンプに変えるため、本質的に顕著な違いを排除します。

例: Jsperf

再帰と反復のどちらを選択するかについては、コードの明快さと単純さについてもっと心配することをお勧めします。

3
Burdock