函数性能优化之《函数缓存》

1,180 阅读3分钟

前言

在开始说优化之前呢,我先说说我遇到的问题。我在之前的一家公司做了一款转盘开奖的小游戏,其中要写一个函数,这个函数大概的作用就是要根据转盘转动的角度,去计算出当前时刻开奖的指针,指向的开奖数字是哪个。这个函数就是一个纯函数,根据传入的角度返回一个当前开奖号码

function getLotteryNum(degrees){ // 0 <=·degrees <= 360
  let lotteryNum = 0
  // 根据转盘转动的角度计算当前的中奖区域号码是哪个
  return lotteryNum
}
时光匆匆,紧赶慢赶游戏做出来了,实现了功能,游戏通过了功能测试,性能呢,每个手机都不一样,所以体验有的好有的坏( PS 心理活动,体验不好的,都是手机不行,别人手机怎么可以)。游戏上线了,才是我的悲剧的开始,各种反馈卡顿,体验差,开奖不及时等等。 那段时间啊,头发大把的掉啊!还时时刻刻被领导吐槽。我开始后悔了,为什么要放松对自己的要求,给别人diss 我的机会。但是问题还是要解决的,我需要开始做优化工作了,《纯函数缓存》是其中一个重要的手段。

什么是函数缓存

函数缓存他的本质就是用空间来换取时间,对于无副作用的纯函数,在合适的场景使用函数缓存是非常有必要的。他快就在于跳过了计算过程,他直接缓存了纯函数的执行结果,当你下一次传入相同的参数,他会在缓存里面读取之前的计算结果返回给你。
### 写一个缓存函数
在这个借鉴一下vue框架源码的函数缓存方法,简单写一下,我们做个试验对比一下性能优化的程度。
// 定义一个全局缓存变量
const cache = Object.create(null)

// 定义一个缓存函数
function cached(fn){
  return (function cachedFn (str) {
    const  hit = cache[str]
    return hit || (cache[str] = fn(str))
  })
}

// 定义一个纯函数
function add(a){
  console.log('纯函数计算', a)
  return (((a + a)/10) * 2) + 8
}
调用高阶函数cached对纯函数缓存
let sum = cached(add)
接下来我们写一段代码开始测试一下他的执行速度
all = 0
start = new Date().getTime()
for(let i = 0; i<10000; i++){
  all =  all + sum(i)
}
console.log(all)
end = new Date().getTime()
console.log('执行时间', end - start)
执行结果对比截图

我们可以看出,在第一次执行的时候,花费的时间为639毫秒,第二次执行的时候为2毫秒。这个时间差距还是很大的。为什么这样呢?我们可以看看我们定义的cache常量都有啥?

当你输入的参数在这个缓存对象里,他就会找到之前的计算结果返回给你。那么这个时候我们是不是就可以得出结论,任何时候使用纯函数都可以用函数缓存这个策略来做优化呢?结论当然不是这个样的。纯函数缓存也不是什么时候都适用。

什么时候不适合用函数缓存

1、对于使用频次比较低的函数就不适用,当你的计算结果长时间占用内存,而你的使用率又不高,这个就造成了浪费。得不偿失。
2、对于本身执行速度较快的函数就不适用。还是这个例子,我们简单修改一下,对比一下执行时间。
function add(a){
  // 去掉 console.log('a', a) 
  return (((a + a)/10) * 2) + 8
}
all = 0
start = new Date().getTime()
for(let i = 0; i<10000; i++){
  all =  all + add(i) // 直接使用纯函数计算
}
console.log(all)
end = new Date().getTime()
console.log('纯函数执行时间', end - start)

和之前的运行结果对比,发现这种不做缓存的方式,函数执行时间更短。

总结一下

1、函数缓存对于性能的提升很有帮助,但是不是所有场景都适用,仅限于计算耗时、需要多次重复计算的纯函数。
2、永远不要放松对自己的要求,欠下的技术债是有利息的。该优化的时候不要手软。
好长时间没写了,文笔不行,如果有错误的地方,欢迎指正,谢谢!