javascript要点

by Rainer Hahnekamp

通过Rainer Hahnekamp

JavaScript要点:为什么您应该知道引擎如何工作 (JavaScript essentials: why you should know how the engine works)

This article is also available in Spanish.

本文还提供西班牙语 。

In this article, I want to explain what a software developer, who uses JavaScript to write applications, should know about engines so that the written code executes properly.

在本文中,我想解释一下使用JavaScript编写应用程序的软件开发人员应该了解哪些引擎,以便所编写的代码能够正确执行。

You’ll see below a one-liner function that returns the property lastName of the passed argument. Just by adding a single property to each object, we end up with a performance drop of more than 700%!

您将在下面看到一个单线函数,该函数返回所传递参数的属性lastName。 仅通过向每个对象添加单个属性,我们最终性能下降超过700%!

As I will explain in detail, JavaScript’s lack of static types drives this behaviour. Once seen as an advantage over other languages like C# or Java, it turns out to be more of a “Faustian bargain”.

正如我将详细解释的那样,JavaScript缺少静态类型会导致这种行为。 一度被视为比其他语言(例如C#或Java)有优势,但事实证明,它更像是“福斯式讨价还价”。

全速刹车 (Braking at Full Speed)

Usually, we don’t need to know the internals of an engine which runs our code. The browser vendors invest heavily in making the engines run code very fast.

通常,我们不需要知道运行我们的代码的引擎的内部。 浏览器供应商在使引擎非常快速地运行代码方面投入了大量资金。

Great!

大!

Let the others do the heavy lifting. Why bother worrying about how the engines work?

让其他人来做繁重的工作。 为什么要担心引擎如何工作?

In our code example below, we have five objects that store the first and last names of Star Wars characters. The function getName returns the value of lastname. We measure the total time this function takes to run 1 billion times:

在下面的代码示例中,我们有五个对象,用于存储《星球大战》角色的名字和姓氏。 函数getName返回姓氏的值。 我们测量此功能运行十亿次所需的总时间:

(() => {   const han = {firstname: "Han", lastname: "Solo"};  const luke = {firstname: "Luke", lastname: "Skywalker"};  const leia = {firstname: "Leia", lastname: "Organa"};  const obi = {firstname: "Obi", lastname: "Wan"};  const yoda = {firstname: "", lastname: "Yoda"};  const people = [    han, luke, leia, obi,     yoda, luke, leia, obi   ];  const getName = (person) => person.lastname;
console.time("engine");  for(var i = 0; i < 1000 * 1000 * 1000; i++) {     getName(people[i & 7]);   }  console.timeEnd("engine"); })();

On an Intel i7 4510U, the execution time is about 1.2 seconds. So far so good. We now add another property to each object and execute it again.

在Intel i7 4510U上,执行时间约为1.2秒。 到目前为止,一切都很好。 现在,我们向每个对象添加另一个属性,然后再次执行它。

(() => {  const han = {    firstname: "Han", lastname: "Solo",     spacecraft: "Falcon"};  const luke = {    firstname: "Luke", lastname: "Skywalker",     job: "Jedi"};  const leia = {    firstname: "Leia", lastname: "Organa",     gender: "female"};  const obi = {    firstname: "Obi", lastname: "Wan",     retired: true};  const yoda = {lastname: "Yoda"};
const people = [    han, luke, leia, obi,     yoda, luke, leia, obi];
const getName = (person) => person.lastname;
console.time("engine");  for(var i = 0; i < 1000 * 1000 * 1000; i++) {    getName(people[i & 7]);  }  console.timeEnd("engine");})();

Our execution time is now 8.5 seconds, which is about a factor of 7 slower than our first version. This feels like hitting the brakes at full speed. How could that happen?

现在我们的执行时间为8.5秒,比我们的第一个版本慢7倍。 感觉就像全速踩刹车。 怎么会这样

Time to take a closer look at the engine.

是时候仔细看看引擎了。

合力:解释器和编译器 (Combined Forces: Interpreter and Compiler)

The engine is the part that reads and executes source code. Each major browser vendor has its own engine. Mozilla Firefox has Spidermonkey, Microsoft Edge has Chakra/ChakraCore and Apple Safari names its engine JavaScriptCore. Google Chrome uses V8, which is also the engine of Node.js.The release of V8 in 2008 marked a pivotal moment in the history of engines. V8 replaced the browser’s relatively slow interpretation of JavaScript.

引擎是读取和执行源代码的部分。 每个主要的浏览器供应商都有其自己的引擎。 Mozilla Firefox使用Spidermonkey,Microsoft Edge使用Chakra / ChakraCore,Apple Safari将其引擎命名为JavaScriptCore。 谷歌浏览器使用V8,它也是Node.js的引擎.2008年V8的发布标志着引擎历史上的关键时刻。 V8取代了浏览器相对较慢JavaScript解释。

The reason behind this massive improvement lies mainly in the combination of interpreter and compiler. Today, all four engines use this technique.The interpreter executes source code almost immediately. The compiler generates machine code which the user’s system executes directly.

进行大规模改进的原因主要在于解释器和编译器的结合。 如今,所有四个引擎都使用这种技术。解释器几乎立即执行源代码。 编译器生成机器代码,用户系统直接执行该机器代码。

As the compiler works on the machine code generation, it applies optimisations. Both compilation and optimisation result in faster code execution despite the extra time needed in the compile phase.

当编译器进行机器代码生成时,它会进行优化。 尽管在编译阶段需要额外的时间,但是编译和优化都会导致更快的代码执行速度。

The main idea behind modern engines is to combine the best of both worlds:

现代引擎背后的主要思想是将两者的优点结合起来:

  • Fast application startup of the interpreter.解释器的快速应用程序启动。
  • Fast execution of the compiler.快速执行编译器。

Achieving both goals starts off with the interpreter. In parallel, the engine flags frequently executed code parts as a “Hot Path” and passes them to the compiler along with contextual information gathered during execution. This process lets the compiler adapt and optimise the code for the current context.

实现两个目标始于口译员。 并行地,引擎将频繁执行的代码部分标记为“热路径”,并将它们与执行期间收集的上下文信息一起传递给编译器。 通过此过程,编译器可以针对当前上下文调整和优化代码。

We call the compiler’s behaviour “Just in Time” or simply JIT.When the engine runs well, you can imagine certain scenarios where JavaScript even outperforms C++. No wonder that most of the engine’s work goes into that “contextual optimisation”.

我们称编译器的行为为“及时”或简称为JIT。当引擎运行良好时,您可以想象某些情况下JavaScript甚至胜过C ++。 难怪引擎的大部分工作都用于“上下文优化”。

运行时静态类型:内联缓存 (Static Types during Runtime: Inline Caching)

Inline Caching, or IC, is a major optimisation technique within JavaScript engines. The interpreter must perform a search before it can access an object’s property. That property can be part of an object’s prototype, have a getter method or even be accessible via a proxy. Searching for the property is quite expensive in terms of execution speed.

内联缓存(IC)是JavaScript引擎中的一项主要优化技术。 解释器必须执行搜索才能访问对象的属性。 该属性可以是对象原型的一部分,可以具有getter方法,甚至可以通过代理进行访问。 就执行速度而言,搜索该属性非常昂贵。

The engine assigns each object to a “type” that it generates during the runtime. V8 calls these “types”, which are not part of the ECMAScript standard, hidden classes or object shapes. For two objects to share the same object shape, both objects must have exactly the same properties in the same order. So an object{firstname: "Han", lastname: "Solo"} would be assigned to a different class than {lastname: "Solo", firstname: "Han"}.

引擎将每个对象分配给它在运行时生成的“类型”。 V8称这些“类型”为ECMAScript标准,隐藏类或对象形状的一部分。 为了使两个对象共享相同的对象形状,两个对象必须具有相同顺序的完全相同的属性。 因此,对象{firstname: "Han", lastname: "Solo"}将被分配{lastname: "Solo", firstname: "Han"}不同的类。

With the help of the object shapes, the engine knows the memory location of each property. The engine hard-codes those locations into the function that accesses the property.

在对象形状的帮助下,引擎知道每个属性的存储位置。 引擎将这些位置硬编码到访问属性的函数中。

What Inline Caching does is eliminate lookup operations. No wonder this produces a huge performance improvement.

内联缓存的作用是消除查找操作。 难怪这会带来巨大的性能提升。

Coming back to our earlier example: All of the objects in the first run only had two properties, firstname and lastname, in the same order. Let’s say the internal name of this object shape is p1. When the compiler applies IC, it presumes that the function only get passed the object shapep1 and returns the value of lastname immediately.

回到前面的示例:第一次运行中的所有对象都只有两个属性,即firstnamelastname ,顺序相同。 假设此对象形状的内部名称为p1 。 当编译器应用IC时,它假定该函数仅通过对象形状p1并立即返回lastname的值。

In the second run, however, we dealt with 5 different object shapes. Each object had an additional property and yoda was missing firstname entirely. What happens once we are dealing with multiple object shapes?

但是,在第二轮中,我们处理了5种不同的对象形状。 每个对象都有一个附加属性,而yoda完全没有firstname 。 一旦我们处理多个对象形状,会发生什么?

介入鸭子或多种类型 (Intervening Ducks or Multiple Types)

Functional programming has the well-known concept of “duck typing” where good code quality calls for functions that can handle multiple types. In our case, as long as the passed object has a property lastname, everything is fine.

函数式编程具有众所周知的“鸭式输入”概念,其中良好的代码质量要求可以处理多种类型的函数。 在我们的例子中,只要传递的对象具有姓氏属性,就可以了。

Inline Caching eliminates the expensive lookup for a property’s memory location. It works best when, at each property access, the object has the same object shape. This is called monomorphic IC.

内联缓存消除了对属性存储位置的昂贵查找。 在每个属性访问中,对象具有相同的对象形状时,其效果最佳。 这称为单态IC。

If we have up to four different object shapes, we are in a polymorphic IC state. Like in monomorphic, the optimised machine code “knows” already all four locations. But it has to check to which one of the four possible object shapes the passed argument belongs. This results in a performance decrease.

如果我们有多达四​​个不同的对象形状,则我们处于多态IC状态。 就像单态一样,优化的机器代码已经“知道”所有四个位置。 但是它必须检查传递的参数属于四个可能的对象形状之一。 这导致性能下降。

Once we exceed the threshold of four, it gets dramatically worse. We are now in a so-called megamorphic IC. In this state, there is no local caching of the memory locations anymore. Instead, it has to be looked up from a global cache. This results in the extreme performance drop we have seen above.

一旦超过四个阈值,情况就会变得更加糟糕。 我们现在处于所谓的大形集成电路中。 在这种状态下,不再存在存储位置的本地缓存。 相反,必须从全局缓存中查找它。 这导致我们上面看到的极端性能下降。

行动中的多态和大态 (Polymorphic and Megamorphic in Action)

Below we see a polymorphic Inline Cache with 2 different object shapes.

在下面,我们看到具有2种不同对象形状的多态Inline Cache。

And the megamorphic IC from our code example with 5 different object shapes:

我们的代码示例中的超大型IC具有5种不同的对象形状:

JavaScript类来解救 (JavaScript Class to the rescue)

OK, so we had 5 object shapes and ran into a megamorphic IC. How can we fix this?

好的,所以我们有5种物体形状,并遇到了一个超大型IC。 我们该如何解决?

We have to make sure that the engine marks all 5 of our objects as the same object shape. That means the objects we create must contain all possible properties. We could use object literals, but I find JavaScript classes the better solution.

我们必须确保引擎将所有5个对象标记为相同的对象形状。 这意味着我们创建的对象必须包含所有可能的属性。 我们可以使用对象文字,但是我发现JavaScript类是更好的解决方案。

For properties that are not defined, we simply pass null or leave it out. The constructor makes sure that these fields are initialised with a value:

对于未定义的属性,我们只需传递null或将其省略。 构造函数确保使用以下值初始化这些字段:

(() => {  class Person {    constructor({      firstname = '',      lastname = '',      spaceship = '',      job = '',      gender = '',      retired = false    } = {}) {      Object.assign(this, {        firstname,        lastname,        spaceship,        job,        gender,        retired      });    }  }
const han = new Person({    firstname: 'Han',    lastname: 'Solo',    spaceship: 'Falcon'  });  const luke = new Person({    firstname: 'Luke',    lastname: 'Skywalker',    job: 'Jedi'  });  const leia = new Person({    firstname: 'Leia',    lastname: 'Organa',    gender: 'female'  });  const obi = new Person({    firstname: 'Obi',    lastname: 'Wan',    retired: true  });  const yoda = new Person({ lastname: 'Yoda' });  const people = [    han,    luke,    leia,    obi,    yoda,    luke,    leia,    obi  ];  const getName = person => person.lastname;  console.time('engine');  for (var i = 0; i < 1000 * 1000 * 1000; i++) {    getName(people[i & 7]);  }  console.timeEnd('engine');})();

When we execute this function again, we see that our execution time returns to 1.2 seconds. Job done!

当我们再次执行此函数时,我们看到执行时间返回到1.2秒。 任务完成!

摘要 (Summary)

Modern JavaScript engines combine the benefits of interpreter and compiler: Fast application startup and fast code execution.

现代JavaScript引擎结合了解释器和编译器的优点:快速的应用程序启动和快速的代码执行。

Inline Caching is a powerful optimisation technique. It works best when only a single object shape passes to the optimised function.

内联缓存是一种强大的优化技术。 当只有单个对象形状传递给优化功能时,它效果最佳。

My drastic example showed the effects of Inline Caching’s different types and the performance penalties of megamorphic caches.

我的一个生动例子展示了Inline Caching的不同类型的影响以及超大型缓存的性能损失。

Using JavaScript classes is good practice. Static typed transpilers, like TypeScript, make monomorphic IC’s more likely.

使用JavaScript类是一个好习惯。 像TypeScript这样的静态类型的编译器使单态IC的可能性更大。

进一步阅读 (Further Reading)

  • David Mark Clements: Performance Killers for TurboShift and Ignition: https://github.com/davidmarkclements/v8-perf

    大卫·马克·克莱门茨(David Mark Clements):TurboShift和点火的性能杀手: https : //github.com/davidmarkclements/v8-perf

  • Victor Felder: JavaScript Engines Hidden Classes

    Victor Felder:JavaScript引擎隐藏类

    Victor Felder: JavaScript Engines Hidden Classeshttps://draft.li/blog/2016/12/22/javascript-engines-hidden-classes

    Victor Felder:JavaScript引擎隐藏类https://draft.li/blog/2016/12/22/javascript-engines-hidden-classes

  • Jörg W. Mittag: Overview of JIT Compiler and Interpreter

    JörgW. Mittag:JIT编译器和解释器概述

    Jörg W. Mittag: Overview of JIT Compiler and Interpreterhttps://softwareengineering.stackexchange.com/questions/246094/understanding-the-differences-traditional-interpreter-jit-compiler-jit-interp/269878#269878

    JörgW.Mittag:JIT编译器和解释器概述https://softwareengineering.stackexchange.com/questions/246094/understanding-the-differences-traditional-interpreter-jit-compiler-jit-interp/269878#269878

  • Vyacheslav Egorov: What’s up with Monomorphism

    维亚切斯拉夫·埃格罗夫(Vyacheslav Egorov):单态现象如何

    Vyacheslav Egorov: What’s up with Monomorphismhttp://mrale.ph/blog/2015/01/11/whats-up-with-monomorphism.html

    维亚切斯拉夫·埃格罗夫(Vyacheslav Egorov):单态论是怎么回事http://mrale.ph/blog/2015/01/11/whats-up-with-monomorphism.html

  • WebComic explaining Google Chrome

    WebComic解释Google Chrome

    WebComic explaining Google Chromehttps://www.google.com/googlebooks/chrome/big_00.html

    WebComic解释Google Chrome https://www.google.com/googlebooks/chrome/big_00.html

  • Huiren Woo: Differences between V8 and ChakraCore

    胡仁仁:V8和ChakraCore之间的区别

    Huiren Woo: Differences between V8 and ChakraCorehttps://developers.redhat.com/blog/2016/05/31/javascript-engine-performance-comparison-v8-charkra-chakra-core-2/

    胡仁仁: V8和ChakraCore之间的区别https://developers.redhat.com/blog/2016/05/31/javascript-engine-performance-comparison-v8-charkra-chakra-core-2/

  • Seth Thompson: V8, Advanced JavaScript, & the Next Performance Frontier

    塞思·汤普森(Seth Thompson):V8,高级JavaScript和下一个性能前沿

    Seth Thompson: V8, Advanced JavaScript, & the Next Performance Frontierhttps://www.youtube.com/watch?v=EdFDJANJJLs

    塞思·汤普森(Seth Thompson):V8,高级JavaScript和下一个性能前沿https://www.youtube.com/watch?v=EdFDJANJJLs

  • Franziska Hinkelmann — Performance Profiling for V8

    Franziska Hinkelmann — V8的性能分析

    Franziska Hinkelmann — Performance Profiling for V8https://www.youtube.com/watch?v=j6LfSlg8Fig

    Franziska Hinkelmann-V8的性能分析https://www.youtube.com/watch?v=j6LfSlg8Fig

  • Benedikt Meurer: An Introduction to Speculative Optimization in V8

    Benedikt Meurer:V8中的投机优化简介

    Benedikt Meurer: An Introduction to Speculative Optimization in V8https://ponyfoo.com/articles/an-introduction-to-speculative-optimization-in-v8

    Benedikt Meurer:V8中的投机优化简介https://ponyfoo.com/articles/an-introduction-to-speculative-optimization-in-v8

  • Mathias Bynens: JavaScript engine fundamentals: Shapes and Inline Caches

    Mathias Bynens:JavaScript引擎基础知识:形状和内联缓存

    Mathias Bynens: JavaScript engine fundamentals: Shapes and Inline Cacheshttps://mathiasbynens.be/notes/shapes-ics

    Mathias Bynens:JavaScript引擎基础知识:形状和内联缓存https://mathiasbynens.be/notes/shapes-ics

翻译自: https://www.freecodecamp.org/news/javascript-essentials-why-you-should-know-how-the-engine-works-c2cc0d321553/

javascript要点

javascript要点_JavaScript要点:为什么您应该知道引擎如何工作相关推荐

  1. javascript 询问_JavaScript解释引擎

    看完本文,你就可以理解下面两个实例的原理. fun() console.log(num)function fun() {num = 20 }// output: 20 var num = 10 fun ...

  2. javascript控制台_JavaScript控制台简介

    javascript控制台 When it fails, JavaScript most often dies with barely a whimper. While this is general ...

  3. JavaScript/Ajax/JQuery知识点(BOM/DOM/ScriptEngine/JS引擎),JSCore

    捋顺JavaScript底层知识,重点讲解如原型.作用域.执行上下文.变量对象.this.闭包.按值传递.call.apply.bind.new.继承等难点概念??   JS中的继承?JS的原型模式, ...

  4. javascript迭代器_JavaScript符号,迭代器,生成器,异步/等待和异步迭代器-全部简单解释...

    javascript迭代器 by rajaraodv 通过rajaraodv JavaScript符号,迭代器,生成器,异步/等待和异步迭代器-全部简单解释 (JavaScript Symbols, ...

  5. javascript闭包_JavaScript闭包教程–带有JS闭包示例代码

    javascript闭包 Closures – many of you JavaScript devs have probably heard this term before. When I sta ...

  6. javascript原型_JavaScript原型初学者指南

    javascript原型 You can't get very far in JavaScript without dealing with objects. They're foundational ...

  7. javascript异步_JavaScript异步并在循环中等待

    javascript异步 Basic async and await is simple. Things get a bit more complicated when you try to use ...

  8. 背包系统 设计要点_建立新设计系统的要点和要点

    背包系统 设计要点 重点 (Top highlight) When I first sat down with my company's CEO and CRO to pitch them on a ...

  9. 计算机系统管理的要点gmp要点,“基于风险管理理念构建生物制品临床 研发GMP体系要点及落地实施策略” (重庆)技术交流会...

    课题一:基于产品生命周期的药品质量管理体系系统的构建要点 1.长生生物事件问题解析 2.制药企业在建立GMP质量管理体系时存在的困惑与迷茫 3.生命周期下的药物质量体系(PQS)构建要点 3.1 诚实 ...

最新文章

  1. 深度学习中的网络表征学习的算法目标简介
  2. jw摄像_Java命令行界面(第17部分):jw-options
  3. 【渝粤题库】广东开放大学 系统工程 形成性考核
  4. MySQL添加、更新、删除数据
  5. 人工智能的炒作_人工智能与网络安全结合从炒作走向现实
  6. ERP核心业务流程和Oracle-ERP业务和数据对象分析
  7. 这些堪称神器的Chrome插件,提升效率不止10倍
  8. PUN 2 菜鸟养成记 2主服务
  9. 计算机网络:第四章网络层课后习题及答案(精细版)
  10. 国际新闻|PostgreSQL 14.3、13.7、12.11、11.16 和 10.21 发布
  11. python 小说爬虫_初次尝试python爬虫,爬取小说网站的小说。
  12. unity之动画编辑器
  13. 低代码助力制造型企业——工时管理系统
  14. apache php gzip压缩输出的实现方法
  15. 为什么需要稀疏编码及解释
  16. eNSP - 华为交换机常用命令
  17. Linux——挂载硬盘
  18. Sublime Text 3默认临时/缓存文件保存位置
  19. iOS 引导图的聚光灯效果代码实现
  20. 萧洁云:SAP中国重上快车道

热门文章

  1. 爬虫-访问用户中心页面-服务端做了些什么
  2. linux-centos连网
  3. django-基本使用
  4. python删除列表中的元素
  5. DirectX - dds图片格式(DDSURFACEDESC2)
  6. python实战===生成随机数
  7. Dapp开发教程四 Asch Dapp Dice Game
  8. 设计模式-依赖倒转模式(面向接口编程)
  9. android通过json生成视图
  10. 周六——中国电影博物馆