从爬楼梯简单谈谈执行性能

爬楼梯这个问题,其实思路是很明确的:爬到当前楼梯的方法数 = 爬到上一级楼梯(然后再爬一级,只有这一种选择)的方法数 + 爬到上两级楼梯(然后再爬两级,只有这一种选择)的方法数,也就是:
f ( x ) = f ( x − 1 ) + f ( x − 2 ) f(x)=f(x-1)+f(x-2) f(x)=f(x1)+f(x2)
这个其实是老生常谈了,它其实就是著名的斐波那契数列,可以循环或者递归求解,也可以用dp来剪枝,然后进一步对dp的数组进行优化(所谓的滚动数组),乃至矩阵快速幂、通项公式,或者更极端的直接打表(如果指定范围的话),就不一一解释了。

这里举个求通项公式的例子。我们可以看到这样一段代码:

function climbStairs(n: number): number {
  return (
    (((1 + Math.sqrt(5)) / 2) ** (n + 1) -
      ((1 - Math.sqrt(5)) / 2) ** (n + 1)) /
    Math.sqrt(5)
  );
}

可以看到,这段代码里有大量的重复计算,比如重复了三次的Math.sqrt(5)。但是,这段代码的执行结果,有时候却比不重复计算的快:

function climbStairs(n: number): number {
  const sqrt5 = Math.sqrt(5);
  return (((1 + sqrt5) / 2) ** (n + 1) - ((1 - sqrt5) / 2) ** (n + 1)) / sqrt5;
}

这个其实牵涉到V8的一些机制。V8作为著名的JIT引擎,会对“热点代码”进行编译,转化成汇编代码。也就是说,反复执行的代码就有概率会被编译成汇编;反复执行就是为了提醒v8,我这个代码你可以优化。即使是重复执行几次这种级别的汇编码,也比只执行一次的需要翻译的JS代码要快。这其实是很有意思的一件事,我在之前的文章里稍微提过一点。

但是整个过程是不能控制的,具体是否会被编译,如何编译,我们都无法控制,类似于数据库的优化器,以及cpp的inline机制,只是一个“请求”而已。并且,如果由于种种原因,编译过的代码被放弃,会造成大量的反优化的开销。所以为了稳定考虑,一般还是会提取一个变量来避免重复运算;毕竟我们不能把宝押在JS引擎上,不是吗。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值