TypeScript中函数后续调用执行速度为何远快于首次调用?
p1: 0.921ms
p1: 0.522ms
p1a: 1.128ms
p1a: 0.485ms
p1a: 0.508ms
如果调换函数的调用顺序,会得到如下输出:
p1a: 3.511ms
p1a: 1.102ms
p1a: 0.468ms
p1: 1.046ms
p1: 0.567ms
p1: 0.542ms
核心问题分为两部分: 1. 为什么完全相同的函数连续运行3次时,后续每次调用的执行速度都会逐次变快? 2. 为什么`p1`在首个调用时运行耗时为3.52ms,但在`p1a`之后调用时仅耗时1.046ms?反过来`p1a`先调用、`p1`后调用时也存在同样的现象,原因是什么? --- ## 解答 ### 问题1解答 该现象由JavaScript引擎(常见为Node.js/Chrome使用的V8引擎)的即时编译(JIT)和运行时优化机制直接导致: - 第一次调用函数时,引擎以解释模式执行字节码,同时收集函数参数、内部变量的类型信息,无任何编译优化,叠加初始化开销所以耗时最长。 - 第二次调用时,函数被识别为热点代码,引擎将其编译为基线机器码,跳过解释步骤直接执行,同时内联缓存(IC)完成预热,`forEach`回调、字符串拆分、数组索引访问等操作的类型判断可以直接命中缓存,不需要动态查询,速度大幅提升。 - 第三次调用时,引擎基于前两次收集的稳定类型信息做激进优化编译,包括小函数内联、冗余类型检查消除、循环优化等,同时输入数据已经常驻CPU高速缓存,内存访问延迟进一步降低,速度达到稳定峰值。 ### 问题2解答 两个函数首次调用的耗时差异,来源于先执行的函数已经完成了大量公共运行时预热工作: - 两个函数核心逻辑高度相似,都用到了字符串拆分数组遍历、数组`forEach`迭代、二进制字符串转数字`parseInt`、`console`输出等通用操作,先执行的函数已经完成了这些通用API的内联缓存初始化、字节码编译,后执行的函数可以直接复用这些优化结果,不需要重复支付冷启动开销。 - 输入的`input`数组在首次访问时会触发操作系统的页缺失(Page Fault),加载完成后会留在物理内存和CPU缓存中,后续不管哪个函数访问这个数组,内存访问速度都会远高于第一次冷加载。 - 两个函数用到的数组、字符串对象的隐藏类(Shape)已经被先执行的函数稳定下来,后续属性访问不需要动态计算偏移量,进一步降低了首次调用的开销。 --- 内容的提问来源于stack exchange,提问作者dsf190
相关产品推荐
相关产品推荐

