今天看啥  ›  专栏  ›  jikequan

(译)V8 JavaScript引擎

jikequan  · 掘金  ·  · 2018-05-07 03:32

(译)V8 JavaScript引擎

改进了代码缓存

V8使用从Chrome 66开始,我们通过在顶层执行后生成缓存来缓存更多代码。这会导致初始加载时分析和编译时间缩短20-40%。

背景

V8使用两种代码缓存来缓存生成的代码,以便以后重用。首先是每个V8实例中都有的内存缓存。初始编译后生成的代码存储在此缓存中,并键入源字符串。这可以在V8的相同实例中重复使用。另一种代码缓存序列化生成的代码并将其存储在磁盘上供将来使用。该缓存并不特定于V8的特定实例,并可用于V8的不同实例。这篇博文主要关注Chrome中使用的第二种代码缓存。(其他嵌入程序也使用这种代码缓存;它不仅限于Chrome,但本博文仅关注Chrome中的使用情况。)

Chrome将序列化的生成代码存储在磁盘缓存中,并使用脚本资源的URL将其键入。加载脚本时,Chrome会检查磁盘缓存。如果脚本已被缓存,则Chrome会将序列化数据作为编译请求的一部分传递给V8。然后V8反序列化这些数据,而不是解析和编译脚本。还有额外的检查来确保代码仍然可用(例如:版本不匹配导致缓存的数据无法使用)。

真实世界的数据显示,代码缓存命中率(对于可以缓存的脚本)很高(〜86%)。虽然这些脚本的缓存命中率很高,但是我们每个脚本缓存的代码量并不是很高。我们的分析表明,增加缓存的代码量可以减少解析和编译JavaScript代码大约40%的时间。

增加缓存的代码量

在以前的方法中,代码缓存与编译脚本的请求相结合。

嵌入者可以请求V8序列化它在顶级编译新的JavaScript源文件时生成的代码。编译脚本后,V8返回了序列化代码。当Chrome再次请求相同的脚本时,V8会从缓存中获取序列化的代码并对其进行反序列化。
V8完全避免了重新编译已经在缓存中的函数。
下图显示了这些场景:

V8仅编译在顶层编译期间预期立即执行的函数(IIFE),并标记用于延迟编译的其他函数。
这有助于通过避免编译不需要的函数来提高页面加载时间,但这意味着序列化数据仅包含急切编译的函数的代码。

在Chrome 59之前,我们必须在任何执行开始之前生成代码缓存。较早的V8基本编译器(Full-codegen)为执行上下文生成专用代码。Full-codegen将代码修补用于特定执行上下文的快速路径操作。通过删除要在其他执行上下文中使用的特定于上下文的数据,不能轻易地序列化此类代码。

随着在Chrome 59中,这一限制不再是必要的。点火使用来执行当前执行环境中的快速路径操作。上下文相关数据存储在反馈向量中并与生成的代码分开。
这已经打开了即使在执行脚本之后也能生成代码缓存的可能性。在我们执行脚本时,会编译更多的函数(标记为惰性编译的函数),从而允许我们缓存更多的代码。

V8公开了一个新API,ScriptCompiler::CreateCodeCache以请求独立于编译请求的代码缓存。请求代码缓存以及编译请求已被弃用,并且不适用于V8 v6.6及更高版本。从版本66开始Chrome使用此API在顶层执行后请求代码缓存。下图显示了请求代码缓存的新场景。代码缓存在顶层执行之后被请求,并因此包含在脚本执行期间稍后被编译的函数的代码。在后面的运行中(在下图中显示为热运行),它避免了在顶层执行期间编译函数。

结果

使用我们内部的测量此功能的性能。下图显示了早期高速缓存方案中分析和编译时间的缩短。在大多数页面上,解析和编译时间都会减少20-40%左右。

来自wild的数据显示了类似的结果,在桌面和移动设备上编译JavaScript代码的时间减少了20-40%。在Android上,这种优化还可以转化为顶级页面加载指标减少1-2%,例如网页成为互动时所需的时间。我们还监测了Chrome的内存和磁盘使用情况,但没有看到任何明显的回归。

发布者Mythri Alle,首席代码Cacher

原文地址:https://v8project.blogspot.com/2018/04/improved-code-caching.html





原文地址:访问原文地址
快照地址: 访问文章快照