javascript - 研究 Chrome 79 中 Javascript DEOPT 原因的宠物项目

标签 javascript google-chrome v8 deoptimization

我一直在修补 Javascript 国际象棋引擎。是的,是的,我知道(笑),这不是这类事情的最佳平台。这有点像一个宠物项目,我很享受学术练习,并且对接近编译语言速度的挑战很感兴趣。 Javascript 中还有其他一些奇怪的挑战,比如缺乏 64 位整数,这使得它不适合国际象棋,但也很有趣。

不久前,我意识到小心构造、函数参数等非常重要。在国际象棋编程中,一切都很重要,但在 Chrome 中通过 Javascript 使用 JIT 编译器 (V8 Turbofan) 时似乎更重要.

通过一些痕迹,我看到了一些急切的 DEOPT,我无法弄清楚如何避免它们。

DEOPT 急切, map 错误

跟踪引用的代码:

if (validMoves.length) { ...do some stuff... }

跟踪直接指向 IF 条件的 validMoves.length 参数。 validMoves 只能是一个空数组 [] 或一个移动对象数组 [{Move},{Move},...]

空数组 [] 会启动 DEOPT 吗?

顺便说一句,我有很多惰性和软 DEOPT,但如果我理解正确的话,这些并不是那么重要,只是 V8 在最终优化代码之前如何围绕我的代码的一部分;在 --trace-opt 中,具有软、惰性 DEOPT 的功能似乎最终会被 Turbofan 优化,并且从长远来看也许不会对性能造成太大影响。 (就此而言,急切的 DEOPT 功能似乎最终也会得到重新优化。)这是一个正确的评估吗?

最后,我有时发现,通过将显示 DEOPT 的函数分解为多个较小的函数调用,我获得了显着的性能提升。由此我推断,较大、较复杂的函数在优化方面遇到了困难,而通过将它们分解,较小的划分函数正在得到优化,从而增加了我的 yield 。听起来合理吗?

最佳答案

the lack of 64bit integers

好吧,现在有 BigInt 了:-) (但在大多数引擎/情况下,它们还不适合高性能操作。)

Would an empty array [] kick off a DEOPT?

一般不会。然而,数组有不同的内部表示,因此可能会也可能不会发生这种情况。

[lazy, soft, eager...] Is this a correct assessment?

一般来说是的。通常您不必担心 deopt,特别是对于早期经历过几次 deopt 的长时间运行的程序。对于 --trace-deopt 报告的所有形容词都是如此 - 这些都只是内部细节。 (“eager”和“lazy”是直接对立的,只是表明必须去优化的函数的激活是否是栈顶的。“soft”是 deopt 的一个特殊原因,即缺乏类型反馈,尽管缺乏类型反馈,V8 还是选择去优化而不是生成“优化”代码,这根本就不是很优化。)

作为一名 JavaScript 开发人员,在极少数情况下您可能会想要关心 deopts。一个例子是,当您遇到相同的 deopt 一次又一次发生的情况时。当这种情况发生时,这是 V8 中的一个错误;这些“deopt循环”很少见,但偶尔也会发生。如果您发现这样的案例,请file a bug带有重现说明。

另一种情况是,每个 CPU 周期都很重要,尤其是在启动期间/在短时间运行的应用程序中,并且一些成本高昂的函数由于可能可以避免的原因而被取消优化。但这似乎不是你的情况。

[breaking up functions...] Does that sound reasonable?

是的,分解函数可能是有益的;特别是如果你开始使用的功能很大的话。一般来说,各种规模的功能都会得到优化;显然更大的函数需要更长的时间来优化。这是一个棘手的领域,没有简单的答案。如果函数那么对性能也没有帮助。 V8 将执行一些内联​​,但决策是基于启发式的,自然并不总是完美的。根据我的经验,手动拆分函数尤其适合长时间运行的循环(您可以将循环放入其自己的函数中)。

编辑:按照要求详细说明最后一点,这里有一个示例:而不是

function big() {
  for (...) {
    // long-running loop
  }
  /* lots more stuff... */
}

您将其拆分为:

function loop() {
  for (...) {
    // same loop as before
  }
}
function outer() {
  loop();
  /* same other stuff as before */
}

对于短循环,这是完全没有必要的,但是如果在循环中花费大量时间并且函数的整体大小很大,那么这种分割允许更精细地进行优化 -颗粒化的 block 和更少的(“软”)deopts。

并且要完全清楚:我只建议您在遇到特定问题时执行此操作(例如:--trace-opt告诉您最大的函数被优化了两次或多次,采取每次一秒钟)。请不要在阅读完这个答案后认为“每个人都应该始终分割自己的职能”,这根本不是我所说的。在极端功能庞大的情况下,将它们拆分可能是有益的。

关于javascript - 研究 Chrome 79 中 Javascript DEOPT 原因的宠物项目,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59433865/

相关文章:

javascript - 正则表达式用 html 文档中的 JavaScript 替换某些类名

javascript - HTML5 视频 : need to find viewed time from "play" to "pause" with javascript

JavaScript 引擎性能基准测试

javascript - 向 'arguments' 类数组对象添加参数,性能测试

javascript - 为什么在 V8 上调用对象字面量的方法会更慢?

javascript - 在 IE 8 中使用 javascript 设置不透明度?

javascript - 实现特定于模板的页面初始化功能

javascript - jQuery 打开文件对话框在 Chrome 和 IE 中多次打开

javascript - 为什么我在 Javascript 中实现寄生继承的努力在这里失败了?

google-chrome - 每次将光标移到视频上时,我都可以隐藏字幕吗?