热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JavaScript事情道理(二):V8引擎和5招高效代碼

本系列的第一篇文章重點引見了引擎,運轉時和挪用棧的概述。第二篇文章將深切V8的JavaScript引擎的內部。我們還會供應一些關於如何編寫更好的JavaScript代碼的技能。概述

本系列的第一篇文章重點引見了引擎運轉時挪用棧的概述。第二篇文章將深切V8的Javascript引擎的內部。我們還會供應一些關於如何編寫更好的Javascript代碼的技能。

概述

Javascript引擎是實行Javascript代碼的遞次或詮釋器。Javascript引擎可以用規範詮釋器(interpreter)或立即編譯器(just-in-time compiler)來完成,立即編譯器以某種情勢將Javascript代碼編譯為字節碼。

盛行的Javascript引擎:

  • V8:開源,Google開闢,C++,Chrome瀏覽器
  • Rhino:開源,Mozilla開闢,Java
  • SpiderMonkey:第一個Javascript引擎,網景瀏覽器(之前)和Firefox(如今)
  • JavascriptCore:開源,蘋果Safari瀏覽器
  • Chakra(JSscript9):Internet Explorer瀏覽器
  • Chakra(Javascript):Microsoft Edge瀏覽器

V8劈頭

V8引擎是由Google構建的,用C++開闢而且開源,與別的的引擎差別的是,V8照樣Node.js的運轉時環境。

《Javascript事情道理(二):V8引擎和5招高效代碼》

V8最初設想用於進步瀏覽器內部Javascript實行的機能。為了取得速率,V8將Javascript代碼轉換為更高效的机械代碼(machine code),而不是運用詮釋器。它經由歷程完成JIT(Just-In-Time)編譯器(如SpiderMonkey或Rhino,等許多當代Javascript引擎)將Javascript代碼編譯為机械代碼。這裏的重要區分在於V8不天生字節碼或任何中心代碼。

V8曾的兩個編譯器

在V8引擎的v5.9版本出來之前,V8有兩個編譯器:
full-codegen:一個簡樸而且速率異常快的編譯器,可以天生簡樸且相對較慢的机械代碼。
Crankshaft:一種更龐雜(Just-In-Time)的優化編譯器,可以天生高度優化的代碼。

V8引擎還在內部運用多個線程:

  • 主線程完成預定的使命:獵取你的代碼,編譯它然後實行它
  • 一個零丁的線程用於編譯,當這個零丁的線程優化代碼時,主線程可以繼承實行
  • 一個Profiler線程,它會通知運轉時我們花了許多時候,使得Crankshaft可以優化它們
  • 一些線程處置懲罰渣滓處置懲罰器掃描

當第一次實行Javascript代碼時,V8應用full-codegen,直接將剖析的Javascript翻譯成机械代碼而無需任何轉換。這使它可以異常疾速地最先實行机械代碼。請注意,V8不運用中心字節碼示意法,不須要詮釋器。

當您的代碼運轉一段時候后,Profiler線程已網絡了充足的數據以肯定哪一種要領應當舉行優化。

接下來,Crankshaft優化從另一個線程最先。它將Javascript籠統語法樹翻譯為稱為Hydrogen的高等靜態單分派(SSA)示意,並嘗試優化該hydrogen圖。大多數優化都是在這個級別完成的。

優化:內聯

第一次優化是提早只管多地嵌入代碼。 內聯是將被挪用函數的主體替代為挪用網站(挪用該函數的代碼行)的歷程。 這個簡樸的步驟可以讓以下優化變得更有意義。

《Javascript事情道理(二):V8引擎和5招高效代碼》

優化:隱蔽的類

Javascript是一種基於原型的言語:沒有類,對象的建立是經由歷程克隆完成的。Javascript也是一種動態編程言語,它意味着屬性可以在實例化后輕鬆增加或從對象中移除。

大多數Javascript詮釋器運用字典式組織(基於哈希函數)來存儲對象屬性值在內存中的位置。這類組織使得檢索Javascript中的屬性的值比在Java或C#等非動態編程言語中的盤算更高貴。在Java中,一切對象屬性都是在編譯之前由牢固的對象規劃肯定的,而且不能在運轉時動態增加或刪除(固然,C#的動態範例是另一個主題)。因而,屬性的值(或指向這些屬性的指針)可以作為一連緩衝區存儲在內存中,每一個值之間都有一個牢固偏移量。偏移量的長度可以依據屬性範例輕鬆肯定,但在運轉時可以變動屬性範例的Javascript中不可行。

由於運用字典查找內存中對象屬性的位置效力異常低,因而V8運用差別的要領:隱蔽類。隱蔽類的事變體式格局與Java等言語中運用的牢固對象規劃(類)相似,除了它們是在運轉時建立的。如今,讓我們看看他們現實的模樣:

function Point(x, y) {
this.x = x;
this.y = y;
}
var p1 = new Point(1, 2);

當“new Point(1, 2)”被實行時, V8引擎會建立一個名為C0的隱蔽類。

《Javascript事情道理(二):V8引擎和5招高效代碼》

由於Point還未定義任何屬性,因而“C0”為空。

一旦實行了第一條語句“this.x = x”(在“Point”函數內部),V8將建立第二個隱蔽類“C1”,它基於“C0”。“C1”形貌了可以找到屬性x的存儲器中的位置(相干於對象指針)。在這類情況下,“x”存儲在偏移量0處,這意味着在內存中將點對象視為一連緩衝區時,第一個偏移量將對應於屬性“x”。 V8還將用“類別轉換”更新“C0”,該類別轉換指出假如將屬性“x”增加到點對象,隱蔽類應從“C0”切換到“C1”。 下面的點對象的隱蔽類如今是“C1”。

《Javascript事情道理(二):V8引擎和5招高效代碼》

每次將新屬性增加到對象時,舊的隱蔽類都邑運用到新隱蔽類的轉換途徑舉行更新。隱蔽類轉換異常重要,由於它們許可隱蔽類在以雷同體式格局建立的對象之間同享。假如兩個對象同享一個隱蔽類並向它們增加了雷同的屬性,則轉換將確保兩個對象都接收到雷同的新隱蔽類以及隨附的一切優化代碼。

當實行語句“this.y = y”(一樣,在“this.x = x”語句以後的Point函數內部)時,將反覆此歷程。

建立一個名為“C2”的新隱蔽類,將類轉換增加到“C1”,指出假如將屬性“y”增加到Point對象(已包括屬性“x”),則隱蔽類應變動為 “C2”,點對象的隱蔽類更新為“C2”。

《Javascript事情道理(二):V8引擎和5招高效代碼》

隱蔽類轉換取決於將屬性增加到對象的遞次。 看看下面的代碼片斷:

function Point(x, y) {
this.x = x;
this.y = y;
}
var p1 = new Point(1, 2);
p1.a = 5;
p1.b = 6;
var p2 = new Point(3, 5);
p2.b = 7;
p2.a = 8;

如今,您能夠以為關於p1和p2,將運用雷同的隱蔽類和轉換。事實上卻不是。關於“p1”,起首增加屬性“a”,然後增加屬性“b”。然則,關於“p2”,起首分派“b”,然後是“a”。 因而,由於差別的轉換途徑,“p1”和“p2”以差別的隱蔽類完畢。在這類情況下,以雷同遞次初始化動態屬性好得多,以便隱蔽的類可以重用。

內聯緩存

V8應用另一種手藝來優化稱為內聯緩存的動態範例化言語。內聯緩存依賴於觀察到對雷同要領的反覆挪用傾向於發作在雷同範例的對象上。在這裏可以找到關於內聯緩存的深切詮釋。

我們將議論內聯緩存的平常觀點(假如您沒有時候經由歷程​​上面的深切詮釋)。

那末它是如何事變的? V8保護一個對象範例的緩存,這些對象在近來的要領挪用中作為參數通報,並運用這些信息來展望將來作為參數通報的對象的範例。假如V8可以對通報給要領的對象的範例做出很好的假定,那末它可以繞過肯定如何接見對象屬性的歷程,而是運用之前查找存儲的信息到對象的隱蔽課程。

那末隱蔽類和內聯緩存的觀點如何相干?不管什麼時候在特定對象上挪用要領,V8引擎都必須實行對該對象的隱蔽類的查找,以肯定接見特定屬性的偏移量。在雷同隱蔽類的兩次勝利挪用以後,V8省略了隱蔽類查找,並簡樸地將該屬性的偏移量增加到對象指針自身。關於該要領的一切將來挪用,V8引擎都假定隱蔽的類沒有變動,並運用從之前的查找存儲的偏移量直接跳轉到特定屬性的內存地址。這大大進步了實行速率。

內聯緩存也是為何雷同範例的對象同享隱蔽類異常重要的緣由。假如您建立兩個具有雷同範例和差別隱蔽類的對象(就像我們之前的示例中那樣),V8將沒法運用內聯緩存,由於縱然這兩個對象的範例雷同,它們對應的隱蔽類為其屬性分派差別的偏移量。

《Javascript事情道理(二):V8引擎和5招高效代碼》

編譯為机械碼

一旦Hydrogen圖被優化,Crankshaft將其降低到稱為Lithium的較初級示意。大部分的Lithium實行都是特定於架構的。寄存器分派發作在這個級別。

終究,Lithium被編譯成机械碼。然後發作其他事變,稱為OSR:客棧替代。在我們最先編譯和優化那些耗時較長的要領之前,我們能夠會運轉它。V8不會遺忘它方才遲緩實行的內容,以再次優化版本最先。相反,它會轉換我們具有的一切上下文(客棧,寄存器),以便我們可以在實行歷程中切換到優化版本。這是一項異常龐雜的使命,考慮到除了其他優化以外,V8最初照樣將代碼內聯。 V8不是唯一可以做到的引擎。

有一種叫做去最佳化的保護措施可以做出相反的改變,並在引擎的假定不再建立的情況下恢復到非優化的代碼。

渣滓網絡

關於渣滓網絡,V8採用了傳統的標記消滅體式格局來清算老一代。標記階段應當住手Javascript實行。為了掌握GC本錢並使實行越發穩固,V8運用增量標記:不是遍歷全部堆,而是試圖標記每一個能夠的對象,它只走過堆的一部分,然後恢復一般實行。下一個GC住手將從先前堆走過的處所繼承。這許可在一般實行時期異常短的停息。如前所述,掃描階段由零丁的線程處置懲罰。

Ignition和TurboFan

跟着2017年早些時候宣布V8 5.9,引入了新的實行流程。這個新的管道在現實的Javascript應用遞次中完成了更大的機能革新和明顯的內存節約。

新的實行流程建立在Ignition,V8的詮釋器和TurboFan,V8的最新優化編譯器之上。

您可以檢察V8團隊關於此主題的博客文章。

自從V8.5版本問世以來,V8團隊一直在勤奮跟上新的Javascript言語特徵,而V8團隊已不再運用V8版本的full-codegen和Crankshaft(自2010年以來服務於V8的手藝)。這些功用須要舉行優化。

這意味着團體V8將有更簡樸和更可保護的架構。

《Javascript事情道理(二):V8引擎和5招高效代碼》

這些革新僅僅是一個最先。 新的Ignition和TurboFan管道為進一步優化鋪平了途徑,這將在將來幾年提拔Javascript機能並減少V8在Chrome和Node.js中的佔用空間。

末了,這裡有一些關於如何編寫優化的,更好的Javascript的技能和秘訣。 您可以輕鬆地從上述內容中獵取這些內容,然則,為了輕易起見,以下是擇要:

如何編寫優化的Javascript

  • 對象屬性的遞次:一直以雷同的遞次實例化對象屬性,以便可以同享隱蔽類和隨後優化的代碼。
  • 動態屬性:在實例化以後向對象增加屬性將強迫隱蔽類變動,並減慢為先前隱蔽類優化的一切要領。相反,在其組織函數中分派一切對象的屬性。
  • 要領:反覆實行雷同要領的代碼將比僅實行一次(由於內聯緩存)實行許多差別要領的代碼運轉得更快。
  • 數組:防止希罕數組,个中的鍵不是增量数字。希罕數組中沒有每一個元素都是哈希表。這類陣列中的元素接見用度較高。別的,只管防止預分派大型數組。跟着你的生長,生長會更好。末了,不要刪除數組中的元素。它使密鑰希罕。
  • 標記值:V8用32位來示意對象和数字。由於它的31位,它運用1個bit來曉得它是一個對象(flag = 1)照樣一個稱為SMI(SMall Integer)的整數(flag = 0)。然後,假如数字值大於31位,V8會將該数字框起來,將其變成雙精度值並建立一個新對象以將該数字放入个中。嘗試只管運用31位有標記数字以防止將高貴的裝箱操縱轉換為JS對象。

推荐阅读
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • PHP图片截取方法及应用实例
    本文介绍了使用PHP动态切割JPEG图片的方法,并提供了应用实例,包括截取视频图、提取文章内容中的图片地址、裁切图片等问题。详细介绍了相关的PHP函数和参数的使用,以及图片切割的具体步骤。同时,还提供了一些注意事项和优化建议。通过本文的学习,读者可以掌握PHP图片截取的技巧,实现自己的需求。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 不同优化算法的比较分析及实验验证
    本文介绍了神经网络优化中常用的优化方法,包括学习率调整和梯度估计修正,并通过实验验证了不同优化算法的效果。实验结果表明,Adam算法在综合考虑学习率调整和梯度估计修正方面表现较好。该研究对于优化神经网络的训练过程具有指导意义。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • Html5-Canvas实现简易的抽奖转盘效果
    本文介绍了如何使用Html5和Canvas标签来实现简易的抽奖转盘效果,同时使用了jQueryRotate.js旋转插件。文章中给出了主要的html和css代码,并展示了实现的基本效果。 ... [详细]
author-avatar
温柔842_259
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有