Cache function to optimize performance

A first look at a simple calculation function, write very common, there is no problem. However, if the incoming value, as there will be double counting Each call will be counted once. We first consider how to avoid double counting?

    let count = 0
    function add(a, b) {
      count ++
      return a + b
    }
    add(7, 8)
    add(7, 8)
    add(7, 8)
    add(7, 8)
    console.log('计算的次数', count) // 计算的次数 4

The following code implements a closure. Which stores the result of the calculation, the same calculation parameters to avoid duplication. 

   function cache(fn) {
      var cacheObj = {}
      return function() {
        // 把arguments伪数组变成真数组
        let argument = Array.prototype.slice.apply(arguments)
        // 对象中如果已经有该参数,就直接去取值 不用计算
        if(cacheObj.hasOwnProperty(argument)) {
          return cacheObj[argument]
        }
        // 如果没有 就调用函数去计算,并把结果存储在cacheObj中
        return cacheObj[argument] = fn.apply(this, argument)
      }
    }
    const cacheAdd = cache(add)
    console.log(cacheAdd(7,8)) // 15
    console.log(cacheAdd(7,8)) // 15
    console.log(cacheAdd(7,8)) // 15
    console.log(cacheAdd(7,8)) // 15
    console.log('计算的次数', count) // 计算的次数 1

Cache function written, here we come to test the performance of these two functions. In theory, the cache function should be a lot faster than a simple add function, the fact is that right?

    let count = 0
    function add(a, b) {
      count ++
      return a + b
    }
    console.time('add')
    for(let i=0; i<10000; i++) {
      add(1, 1)
    }
    console.timeEnd('add') // 执行次数 10000
    console.log('执行次数', count)
    // add: 0.77294921875ms 不太稳定
    let count = 0
    function add(a, b) {
      count ++
      return a + b
    }
    const cacheAdd = cache(add)
    console.time('cacheAdd')
    for(let i=0; i<10000; i++) {
      cacheAdd(1, 1)
    }
    console.timeEnd('cacheAdd') // 执行次数 1
    console.log('执行次数', count)
    // cacheAdd: 17.5810546875ms

Compare the time we found the cache function is not faster than non-cached function, on the contrary, there will be a lot slower than non-cached function. Why is this?

Join us to function changed a bit, changed a * b, passing large numeric values, what happens?

    let count = 0
    function add(a, b) {
      count ++
      return a * b
    }
    const cacheAdd = cache(add)
    console.time('cacheAdd')
    for(let i=0; i<10000; i++) {
      cacheAdd(645, 893)
    }
    console.timeEnd('cacheAdd') // 执行次数 1
    console.log('执行次数', count)
    // cacheAdd: 14.232177734375ms
    let count = 0
    function add(a, b) {
      count ++
      return a * b
    }
    // const cacheAdd = cache(add)
    console.time('add')
    for(let i=0; i<10000; i++) {
      // cacheAdd(645, 893)
      add(645, 893)
    }
    console.timeEnd('add') // 执行次数 10000
    console.log('执行次数', count)
    // add: 0.810791015625ms

However, there has been no result we want, that's not to say that the cache function does not it? See the examples below

  /* 
      这是一个生成斐波纳契数列的函数,传入的参数,就是数列在第几位上的值
      在数学上,斐波纳契数列以如下被以递推的方法定义:F(1)=1,F(2)=2, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*)
    */ 
    let count = 0
    function Fibonacci(n) {
      count++
      return n <= 2 ? 1 : Fibonacci(n-1) + Fibonacci(n-2)
    }
    console.time('Fibonacci')
    Fibonacci(30)
    console.timeEnd('Fibonacci') // Fibonacci: 10.988037109375ms
    console.log('执行次数', count) // 执行次数 1664079
    console.log(Fibonacci(30)) // 832040

I passed 30, you can try what happens if you pass 50? The browser will get stuck, I have been going round in circles.

    let count = 0
    function Fibonacci(n) {
      count++
      return n <= 2 ? 1 : cacheFibonacci(n-1) + cacheFibonacci(n-2)
    }
    function cache(fn) {
      var cacheObj = {}
      return function() {
        // 把arguments伪数组变成真数组
        let argument = Array.prototype.slice.apply(arguments)
        // 对象中如果已经有该参数,就直接去取值 不用计算
        if(cacheObj.hasOwnProperty(argument)) {
          return cacheObj[argument]
        }
        // 如果没有 就调用函数去计算,并把结果存储在cacheObj中
        return cacheObj[argument] = fn.apply(this, argument)
      }
    }
    let cacheFibonacci = cache(Fibonacci)
    console.time('cacheFibonacci')
    cacheFibonacci(30)
    console.timeEnd('cacheFibonacci') // cacheFibonacci: 0.118896484375ms
    console.log('执行次数', count) // 执行次数 30
    console.log(cacheFibonacci(30)) // 832040

The difference has become apparent that run a lot faster. The number of calculations also a lot smaller, so we calculate the first 100 Fibonacci number value is not a problem.

2 Above we personally verify the benefits of caching functions, then we write caching function there any problem? Is not one hundred percent correct values are returned back to it? See the examples below

    function add(obj) {
      return obj.a
    }
    function cache(fn) {
      var cacheObj = {}
      return function() {
        // 把arguments伪数组变成真数组
        let argument = Array.prototype.slice.apply(arguments)
        // 对象中如果已经有该参数,就直接去取值 不用计算
        if(cacheObj.hasOwnProperty(argument)) {
          return cacheObj[argument]
        }
        // 如果没有 就调用函数去计算,并把结果存储在cacheObj中
        return cacheObj[argument] = fn.apply(this, argument)
      }
    }
    let cacheAdd = cache(add)
    console.log(cacheAdd({a:1})) // 1
    console.log(cacheAdd({a:2})) // 1
    console.log(cacheAdd({a:3})) // 1

Parameter passed is an object, the value returned is every 1 Why is that? I look at the door cacheObj what?

         console.log(cacheObj) 
        /* {[object Object]: 1}
          [object Object]: 1
          __proto__: Object
          */

CacheObj can see only one property, [object, object], because the parameters as the key storage, key in the form of a character string in a subject, there is a recluse conversion, key generation process, also called the toString object method, parameters are converted into [object, object]. Every execution will pick up the first down to keep the value, so they can only get a result. We can change the code in Array.prototype.slice.apply (arguments), JSON.stringfy (Array.prototype.slice.apply (arguments)) on it.

    let count = 0
    function add(obj) {
      count++
      return obj.a
    }
    function cache(fn) {
      var cacheObj = {}
      return function() {
        // 把arguments伪数组变成真数组
        let argument = Array.prototype.slice.apply(arguments)
        let argumentString = JSON.stringify(Array.prototype.slice.apply(arguments))
        console.log(cacheObj)
        // 对象中如果已经有该参数,就直接去取值 不用计算
        if(cacheObj.hasOwnProperty(argument)) {
          return cacheObj[argumentString]
        }
        // 如果没有 就调用函数去计算,并把结果存储在cacheObj中
        return cacheObj[argumentString] = fn.apply(this, argument)
      }
    }
    let cacheAdd = cache(add)
    console.log(cacheAdd({a:1, b:3, c:4})) // 1
    console.log(cacheAdd({a:1, c:4, b:3})) // 1
    console.log(cacheAdd({c:1, b:3, a:3})) // 1
    console.log('执行次数', count) // 执行次数 3

Look at the example above, I pass the object is the same, is to change the order in which the function is executed three times, and we hope that the implementation of a value. Print Click cacheObj find {[{ "a": 1, "b": 3, "c": 4}]: 1, [{ "a": 1, "c": 4, "b": 3} ]: 1} the result is this. So when using the cache function, passing parameters to pay attention to the problem.

Guess you like

Origin blog.csdn.net/qq_41831345/article/details/93631575