成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

如何將 DevTools的堆棧追蹤速度提高10倍

開發 前端
天分享的內容是如何將Chrome DevTools的堆棧追蹤速度提高了10倍。之前聽工作6年的同事說,DevTools會影響頁面性能。正好逛國外社區的時候,發現這篇不錯的文章,借此機會分享給大家。

[[399057]]

本文轉載自微信公眾號「TianTianUp」,作者小弋。轉載本文請聯系TianTianUp公眾號。

大家好,我是TianTian。

今天分享的內容是如何將Chrome DevTools的堆棧追蹤速度提高了10倍。

之前聽工作6年的同事說,DevTools會影響頁面性能。正好逛國外社區的時候,發現這篇不錯的文章,借此機會分享給大家。

正文

Web開發人員已經開始期待在調試他們的代碼時幾乎沒有性能影響。然而,這種期望絕不是普遍的。一個C++開發人員永遠不會期望他們的應用程序的調試構建能夠達到生產性能,而在Chrome瀏覽器的早期,僅僅是打開DevTools就會大大影響頁面的性能。

現在已經感覺不到這種性能下降了,這是多年來對DevTools和V8的調試能力投資的結果。盡管如此,我們永遠無法將DevTools的性能開銷降低到零。設置斷點、踏過代碼、收集堆棧痕跡、捕獲性能跟蹤等等,都會在不同程度上影響執行速度。畢竟,觀察到的東西會改變它。

但當然,DevTools的開銷--像任何調試器一樣--應該是合理的。最近我們看到,在某些情況下,DevTools會拖慢應用程序的速度,以至于它不能再使用的報告數量顯著增加。下面你可以看到一個來自報告chromium:1069425的并排比較,說明了僅僅打開DevTools的性能開銷。

報告chromium:1069425鏈接點這里:

  1. https://bugs.chromium.org/p/chromium/issues/detail?id=1069425 

查看這個視頻:

正如你從視頻中看到的,速度降低了5-10倍,這顯然是不可接受的。

第一步是要了解所有的時間都去哪兒了,是什么導致了DevTools打開時的巨大速度下降。

在Chrome渲染器進程上使用Linux perf,發現整個渲染器執行時間的分布如下。

圖1

雖然我們有點期待看到與收集堆棧痕跡有關的東西,但我們不會想到,整個執行時間的大約90%都用于符號化堆棧幀。

這里的符號化是指從原始堆棧幀中解析函數名和具體的源位置--腳本中的行號和列號的行為。

方法名推斷

更令人驚訝的是,幾乎所有的時間都流向了V8中的JSStackFrame::GetMethodName()函數。

盡管我們從以前的調查中知道,JSStackFrame::GetMethodName()在性能問題的土地上并不陌生。

這個函數試圖為那些被認為是方法調用的框架(代表obj.func()而不是func()形式的函數調用的框架)計算方法的名稱。

快速查看代碼發現,它的工作原理是對對象及其原型鏈進行全面的遍歷,并尋找:

  • 數據屬性,其值是func的閉包
  • 訪問器屬性,其中get或set等同于func閉包。

現在,雖然這本身聽起來并不特別便宜,但它也聽起來不像是能解釋這種可怕的減速。

因此,我們開始挖掘chromium:1069425中報告的例子,我們發現堆棧痕跡是為異步任務以及來自classes.js的日志信息收集的,這是一個10MB的JavaScript文件。

仔細觀察發現,這基本上是一個Java運行時,加上編譯成JavaScript的應用程序代碼。堆棧跟蹤包含了幾個框架,其中有一些方法被調用到一個對象A上,所以我們認為可能值得了解我們正在處理的是哪種對象。

chromium:1069425 : https://bugs.chromium.org/p/chromium/issues/detail?id=1069425

圖2

顯然,從Java到JavaScript的編譯器產生了一個對象,上面有高達82,203個函數。

這顯然開始變得有趣了。接下來我們回到V8的JSStackFrame::GetMethodName(),以了解是否有一些低垂的果實可以被我們采摘。

  • 它的工作原理是首先將函數的 "名字 "作為對象的一個屬性進行查找,如果找到了,則檢查該屬性的值是否與該函數相匹配。
  • 如果函數沒有名字,或者對象沒有匹配的屬性,它就會通過遍歷對象的所有屬性及其原型來進行反向查找。

在我們的示例中,所有函數都是匿名的,并且具有空的“名稱”屬性。

  1. A.SDV = function() { 
  2.    // ... 
  3. }; 

最初的發現是將反向查找分為兩個步驟(針對對象本身及其原型鏈中的每個對象執行):

  • 提取所有可枚舉屬性的名稱,然后
  • 對每個名稱執行通用屬性查找,測試結果屬性值是否與我們要查找的閉包相匹配。

這看起來是一個低效的操作,因為提取名字需要走遍所有的屬性。與其做兩遍--O(N)的名字提取和O(N log(N))的測試,我們可以在單遍中完成所有工作,并直接檢查屬性值。這使得整個函數的速度提高了2-10倍左右。

第二個發現甚至更有趣。雖然這些函數在技術上是匿名函數,但V8引擎還是為它們記錄了我們稱之為推斷的名稱。對于以obj.foo = function() {...}形式出現在賦值右側的函數字面,V8解析器會記住 "obj.foo "作為該函數字面的推斷名稱。

因此,在我們的例子中,雖然我們沒有可以直接查找的正確名稱,但我們確實有足夠接近的東西。對于上面的A.SDV = function() {...}例子,我們有 "A.SDV "作為推斷名稱,我們可以通過尋找最后一個點從推斷名稱中得出屬性名稱,然后去尋找對象上的屬性 "SDV"。

這幾乎在所有的情況下都起到了作用,用單一的屬性查找取代了昂貴的全面遍歷。這兩項改進是CL的一部分,大大降低了chromium:1069425中報告的例子的速度下降。

錯誤堆棧

我們本可以在這里收工了,但是有些事情是不對勁的,因為DevTools從來不使用堆棧框架的方法名。事實上,C++ API中的v8::StackFrame類甚至沒有提供獲取方法名稱的方法。因此,我們最終會首先調用JSStackFrame::GetMethodName(),這似乎是錯誤的。

相反,我們使用(和公開)方法名稱的唯一地方是在JavaScript堆棧跟蹤API中。為了理解這種用法,請考慮下面這個簡單的例子error-methodname.js:

  1. function foo() { 
  2.     console.log((new Error).stack); 
  3.  
  4. var object = {bar: foo}; 
  5. object.bar(); 

這里我們有一個函數foo,它被安裝在對象的名稱 "bar "下。在Chromium中運行這個片段,會產生以下輸出:

  1. Error 
  2.     at Object.foo [as bar] (error-methodname.js:2) 
  3.     at error-methodname.js:6 

在這里,我們看到了方法名查找的作用。最上面的堆棧框架被顯示為通過名為bar的方法在Object的實例上調用函數foo。所以非標準的error.stack屬性大量使用了JSStackFrame::GetMethodName(),事實上,我們的性能測試也表明,我們的改變使事情大大加快。

圖3

但回到Chrome DevTools的話題上,即使沒有使用error.stack,方法名稱也被計算出來了,這看起來不對。

這里有一些歷史可以幫助我們:

傳統上,V8有兩種不同的機制來收集和表示上述兩種不同的API(C++ v8::StackFrame API和JavaScript stack trace API)的堆棧跟蹤。有兩種不同的方式來做(大致)相同的事情是容易出錯的,并且經常導致不一致和錯誤,所以在2018年底,我們啟動了一個項目,以解決堆棧跟蹤捕獲的單一瓶頸。

這個項目非常成功,大大減少了與堆棧跟蹤收集有關的問題的數量。大部分通過非標準的error.stack屬性提供的信息也是懶散地計算的,只有在真正需要的時候才會計算,但作為重構的一部分,我們對v8::StackFrame對象采用了同樣的技巧。

所有關于堆棧框架的信息都是在任何方法第一次被調用時計算的。

這通常會提高性能,但不幸的是,它被證明與Chromium和DevTools中使用這些C++ API對象的方式有些相反。

特別是由于我們引入了一個新的v8::internal::StackFrameInfo類,它持有關于堆棧框架的所有信息,這些信息通過v8::StackFrame或通過error.stack公開,我們總是計算兩個API提供的信息的超集,這意味著對于v8::StackFrame的使用(特別是對于DevTools),我們也將計算方法名稱,只要關于堆棧框架的任何信息被請求。事實證明,DevTools總是立即請求源和腳本信息。

基于這一認識,我們能夠重構并大幅簡化堆棧框架的表示,并使其更加懶惰,因此整個V8和Chromium的使用現在只需要支付計算他們所要求的信息的成本。這給DevTools和其他Chromium用例帶來了巨大的性能提升,它們只需要關于堆棧框架的一小部分信息(基本上只是腳本名稱和以行和列偏移形式存在的源位置),并為更多的性能改進打開了大門。

函數名稱

隨著上述重構的完成,符號化的開銷(在v8_inspector::V8Debugger::symbolize中花費的時間)被減少到整個執行時間的15%左右,而且我們可以更清楚地看到V8在(收集和)符號化堆棧幀以便在DevTools中使用時的時間。

[[399058]]

圖4

第一件引人注目的事情是計算行數和列數的累積成本。

這里昂貴的部分實際上是計算腳本中的字符偏移量(基于我們從V8得到的字節碼偏移量),結果發現,由于我們上面的重構,我們做了兩次,一次是在計算行號時,另一次是在計算列號時。

在v8::internal::StackFrameInfo實例上緩存源位置有助于快速解決這個問題,并且完全消除了v8::internal::StackFrameInfo::GetColumnNumber的任何配置文件。

對我們來說,更有趣的發現是,v8::StackFrame::GetFunctionName在我們看的所有配置文件中都出奇地高。深入研究后我們發現,計算我們在DevTools中為堆棧框架中的函數顯示的名稱是不必要的,成本很高。

  • 首先尋找非標準的 "displayName "屬性,如果它產生了一個具有字符串值的數據屬性,我們就會使用它。
  • 否則就返回到尋找標準的 "name "屬性,并再次檢查該屬性是否產生了一個值為字符串的數據屬性。
  • 并最終返回到由V8解析器推斷出的、存儲在函數字面上的內部調試名稱。

"displayName "屬性是為了解決函數實例上的 "name "屬性在JavaScript中只讀且不可配置的問題而添加的,但它從未被標準化,也沒有被廣泛使用,因為瀏覽器的開發工具添加了函數名稱推理,在99.9%的情況下都能完成工作。

除此之外,ES2015讓Function實例上的 "name "屬性變得可配置,完全消除了對特殊 "displayName "屬性的需求。

由于 "displayName "的負向查找成本很高,而且并不是真的需要(ES2015是在五年前發布的),我們決定從V8(和DevTools)中刪除對非標準的fn.displayName屬性的支持。

隨著 "displayName "的負向查找的完成,v8::StackFrame::GetFunctionName的一半成本被移除。另一半則用于通用的 "name "屬性查詢。幸運的是,我們已經有了一些邏輯來避免在(未觸及的)Function實例上進行昂貴的 "name "屬性查詢,我們在不久前的V8中引入了這一邏輯,以使Function.prototype.bind()本身更快。我們移植了必要的檢查,允許我們首先跳過昂貴的通用查詢,結果是v8::StackFrame::GetFunctionName不再出現在我們考慮的任何配置文件中。

總結

通過上述改進,我們已經大大減少了DevTools在堆棧跟蹤方面的開銷。

視頻

我們知道仍有各種可能的改進。

例如,使用MutationObservers時的開銷仍然很明顯,正如chromium:1077657所報告的那樣,但就目前而言,我們已經解決了主要的痛點,而且我們將來可能會回來進一步簡化調試性能。

 

chromium:1077657: https://bugs.chromium.org/p/chromium/issues/detail?id=1077657

 

責任編輯:武曉燕 來源: TianTianUp
相關推薦

2017-10-20 10:09:01

代碼CocoaPods編譯

2017-05-11 11:30:43

MySQL查詢速度

2020-02-28 09:26:54

PythonGo語言C語言

2022-04-27 09:24:22

前端代碼速度

2017-05-10 16:09:12

MySQL數據庫查詢

2020-02-14 09:40:14

人工智能機器學習技術

2009-03-30 14:12:38

LinuxUnladenSwallow

2011-05-30 13:28:00

PHP

2021-07-21 17:03:35

Chrome網絡釣魚瀏覽器

2018-07-30 15:05:26

Hadoop大數據集群

2024-06-27 11:00:07

2016-09-07 15:02:03

ElasticSear索引速度

2023-05-04 07:34:37

Rust代碼CPU

2020-03-13 19:00:38

Windows 10Windows開機速度

2020-10-29 09:06:56

開發工具技術

2020-08-21 10:59:10

微軟服務器運維

2019-09-24 09:25:05

Vue項目加載

2011-05-19 11:33:38

數據庫訪問速度

2020-06-03 11:26:05

算法移動設技術

2021-01-28 08:00:00

Windows 10Windows微軟
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 色橹橹欧美在线观看视频高清 | 久久久精品一区 | 国产一区二区三区四区在线观看 | 无吗视频 | 天天拍天天色 | 91精品国产99 | 午夜手机在线 | 亚洲免费成人 | 欧美国产精品一区二区三区 | a在线观看 | 午夜视频网 | aaaaaa大片免费看最大的 | 久久久www成人免费无遮挡大片 | 麻豆精品国产免费 | 国内精品成人 | 久久久久久亚洲国产精品 | 在线国产一区二区 | 国产最新网址 | 国产精品久久久久久久免费大片 | 亚洲视频免费观看 | 欧美激情久久久 | 日韩免费中文字幕 | 日韩成人在线免费观看 | 久久一二区 | 日韩精品在线一区 | 久久久精品 | 欧美性一区二区三区 | 西西裸体做爰视频 | 欧美一二三 | 日韩欧美一级精品久久 | 亚洲精品电影网在线观看 | 国产久 | 日韩午夜电影在线观看 | 欧美视频在线看 | 成人三级视频 | 麻豆视频国产在线观看 | 中文字幕综合在线 | 日本高清视频在线播放 | 欧美老少妇一级特黄一片 | 美女久久久久久久久 | 欧产日产国产精品国产 |