JSメモ化と関数アクセラレーション

生産性を追求するために、開発者はプログラムを最適化するさまざまな方法を発明します。 私たちの場合、関数の速度を上げることについて話しています。 おそらくJavaScriptでは、言語の礎石の1つと呼ぶことができます。 特に、関数はプログラムをモジュールに分解する手段であり、コードを再利用するためのツールです。

一部の機能は非常に高速に実行されるため、システムに負担をかけますが、繰り返し呼び出しても問題はありません。 一部は非常に「重い」ため、そのような関数を呼び出すたびに、コンピューティングリソースの深刻なオーバーヘッドが発生します。 費用が正当化されれば、計算は最適化され、どこにも行けなくなります。 しかし、繰り返し呼び出し中に、関数が以前の呼び出し中に実行されたのと同じ計算を実行する場合(または非常に頻繁に)はどうでしょうか。 これを使用して生産性を向上できますか?



階乗計算とキャッシング


階乗計算関数は、リソースを集中的に使用する関数の例であり、ほぼ確実に、いくつかの呼び出し中に同じ計算の一部を何度も実行します。 これにより、キャッシングによる最適化の機会が開かれます。

たとえば、数値の階乗を計算して返すfactorial関数は次のfactorialです。 実装の詳細に触れない場合、次のようになります。

 function factorial(n) {   // : n * (n-1) * (n-2) * ... (2) * (1)   return factorial } 

次のように呼び出します: factorial(50) 。 予想通り、彼女は数値50の階乗を見つけて返します。これはすべて良いのですが、今度は彼女の助けを借りて数値51の階乗を見つけましょう。コンピューターが再び計算を実行し、必要なものを見つけます。 ただし、再度呼び出すと、関数は以前に実行された多くの計算を実行することに気付くかもしれません。 関数を最適化してみましょう。 値をfactorial(50) 、関数を再度呼び出さずにfactorial(51)進む方法を考えてみましょう。 階乗の計算式に従うと、 factorial(51)factorial(50) * 51と同じであることがfactorial(50) * 51ます。

ただし、このアプローチでは、パフォーマンスを向上させることはできません。 つまり、最初にfactorial()関数内で、 factorial() 50を見つけるために計算の完全なチェーンが実行され、次に何が起こったのか51が乗算されます。つまり、同様の関数を使用すると、数値51の階乗計算はすべての乗算のように見えます1から51までの数字。

factorial()関数が前の呼び出しで実行された計算の結果を記憶し、パフォーマンスを高速化するために次の呼び出しでそれらを使用できればいいでしょう。

メモ化の基本


以前の関数呼び出しの結果を保存するという質問に答えると、メモ化のアイデアに到達します。 これは、関数が結果を保存(またはキャッシュ)するために使用できる手法です。 一般的な用語で、達成したいことを理解できたので、 メモ化のより厳密な定義を次に示します。
メモ化-繰り返し計算を防ぐために、関数の実行結果を保存します。 これは、コンピュータープログラムの速度を上げるために使用される最適化方法の1つです。

簡単に言えば、メモ化は記憶であり、何かを記憶に保存することです。 メモ化を使用する関数は、通常、より高速に動作します。同じパラメータで再度呼び出されると、一部の計算を実行する代わりに、キャッシュから結果を読み取って返すだけだからです。

簡単なメモ化関数は次のようになります。 このコードはCodePenにあるため、すぐに試してみることができます。

 //  ,  10     const add = (n) => (n + 10); add(9); //     const memoizedAdd = () => { let cache = {}; return (n) => {   if (n in cache) {     console.log('Fetching from cache');     return cache[n];   }   else {     console.log('Calculating result');     let result = n + 10;     cache[n] = result;     return result;   } } } //    memoizedAdd const newAdd = memoizedAdd(); console.log(newAdd(9)); //  console.log(newAdd(9)); //    

メモ化による機能コード分析


上記のコードフラグメントを分析した後、次の結論を導き出すことができます。


メモ化を伴う関数の作成


上記のコードは正常に機能しますが、メモ機能を使用して関数をそのバージョンに変換する場合はどうでしょうか。 そのような関数を書く方法は次のとおりです。 このコードも、 CodePenにあります。

 //   ,      10 const add = (n) => (n + 10); console.log('Simple call', add(3)); //  ,     //   ,    const memoize = (fn) => { let cache = {}; return (...args) => {   let n = args[0];  //        if (n in cache) {     console.log('Fetching from cache');     return cache[n];   }   else {     console.log('Calculating result');     let result = fn(n);     cache[n] = result;     return result;   } } } //        'add' const memoizedAdd = memoize(add); console.log(memoizedAdd(3));  //  console.log(memoizedAdd(3));  //    console.log(memoizedAdd(4));  //  console.log(memoizedAdd(4));  //    

いいね! 私たちのメモ化機能は、メモ化により他の機能を同等のものに変えることができます。 もちろん、このコードは普遍的ではありませんが、 memoize関数が任意の数の引数を持つ関数で機能できるように、やり直すことは難しくありません。

これは自分で作成できますが、ライブラリソリューションがあります。


再帰関数のメモ化


memoize関数の再帰関数、または上記で説明したLodashの_.memoize関数を渡そうとすると、再帰関数が自分自身を呼び出し、メモ化機能を追加した後の動作ではなく、何が起こるかが正しく動作しません。 その結果、この状況でのcache変数はその目的を果たしません。 この問題を解決するために、再帰関数はメモ化を使用して独自のバリアントを呼び出す必要があります。 再帰的階乗関数にメモ化を追加する方法は次のとおりです。 通常のコードはCodePenにあります。

 //     memoize const memoize = (fn) => { let cache = {}; return (...args) => {   let n = args[0];   if (n in cache) {     console.log('Fetching from cache', n);     return cache[n];   }   else {     console.log('Calculating result', n);     let result = fn(n);     cache[n] = result;     return result;   } } } const factorial = memoize( (x) => {   if (x === 0) {     return 1;   }   else {     return x * factorial(x - 1);   } } ); console.log(factorial(5)); //  console.log(factorial(6)); //   6,        

このコードを分析して、次の結論を導き出すことができます。


メモ化とキャッシュについて


メモ化は一種のキャッシュです。 通常、キャッシングとは、後で使用するために何かを保存するかなり幅広い方法を指します。 たとえば、HTTPキャッシングです。 メモ化は通常、関数の戻り値をキャッシュすることを意味します。

結果:メモ化に頼るべきとき


メモ化の手法は非常に優れていると思われるかもしれませんが、すべての機能の一部になることができ、またそうなるはずですが、実際には使用が制限されています。 メモ化の使用に関する考慮事項を次に示します。


親愛なる読者! 実際のプロジェクトでメモ化の使用例がある場合は、共有してください。 多くの人が彼らについて知りたいと思うでしょう。

Source: https://habr.com/ru/post/J332384/


All Articles