本文翻譯自 Chrome DevTools: Show native functions in JS Profile,中文版首發在個人知乎專欄 V8 源碼及周邊。javascript
在 Chrome DevTools 中可使用 profiler 查看原生函數的執行性能:html
原生函數(native function)是 JavaScript 語言的一部分,這些函數有別於開發者編寫的自定義函數。當咱們在 profiler 中查看代碼的調用棧時,這些函數是被過濾掉的。咱們在 profiler 中看到的只有本身寫的代碼。前端
當咱們捕獲調用棧時,Chrome 並不會捕獲 C++ 寫的函數。不過,在 V8 引擎中不少 javascript 原生函數都是使用 javascript 語言編寫的。java
V8 使用 JavaScript 自己實現了 JavaScript 語言的大部份內置對象和函數。 例如,promise 功能就是經過 JavaScript 編寫的。咱們把這樣的內置函數稱爲自主託管(self-hosted)。程序員
若是咱們開啓 「Show native functions」 設置,Chrome 將會在 profiler 中顯示這些函數。web
爲了找到那些耗時最多的代碼,Chrome 分析器每 100μs 捕獲一個堆棧跟蹤。chrome
這意味着,若是一個函數只須要 50μs 的執行時間,就可能不會在分析器中顯示出來!segmentfault
當你分析幾毫秒以上的時間時,能夠準確瞭解應用程序在什麼時候花費最多的時間。 可是,當你放大 profiler 面板想看更精準的時間時,信息會變得不太準確。數組
分析器也會不一致。 每次運行時,會產生一個稍微不一樣的結果。 有時可能會記錄很是短的函數調用,而在其餘時間再次運行這些函數調用信息可能會丟失。promise
經過這篇博客文章我將爲你們演示如何捕獲並分析原生函數的性能。當你本身運行代碼時,結果可能會有所不一樣。
首先,咱們運行以下代碼:
var arr = []
for (var i=0; i<1000; i++){
arr.push(i)
}
console.profile("Array.join")
arr.join(",")
console.profileEnd("Array.join")複製代碼
選擇 profiler 的 「Chart」 視圖:
第一次分析時,咱們不選中 「Show native functions」:
咱們再次運行時,把 「Show native functions」 啓用:
當咱們把鼠標指向函數時,會看到更加詳細的信息:
如上信息中,chrome devtools 展現了原生函數的行號,你能夠在 Chrome code search中找到這個文件 「array.js」。行號信息可能不一樣,由於 V8 源碼的最新版本和 Chrome 使用的 V8 版本可能不同。
你能夠看到 ArrayJoin
函數在內部調用了 InnerArrayJoin
:
function ArrayJoin(separator) {
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.join");
var array = TO_OBJECT(this);
var length = TO_LENGTH(array.length);
return InnerArrayJoin(separator, array, length);
}複製代碼
InnerArrayJoin
在內部調用了 DoJoin
。
DoJoin
調用了 %StringBuilderJoin
。
%StringBuilderJoin
是使用 C++ 實現的。
咱們有點偏離主題,可是我認爲 V8 處理稀疏數組(new Array(n))是很是有趣的。
下面的代碼是如何運行的?
arr = new Array(10000000)
for (var i=0; i<10000; i++){
arr.push(i)
}
console.profile("arr + arr")
arr + arr
console.profileEnd("arr + arr")複製代碼
您一般不會在兩個數組上執行加操做。可是因爲某種緣由,我最近看過的一些代碼就是這樣作的。
當不是用查看原生函數時,咱們看到了一個匿名函數的調用。
當咱們開啓了查看原生函數功能時,Chrome 調用了 array
的 toString
方法,而後調用了 join
方法。
咱們來看一個不一樣的例子。在 JavaScript 中,您可使用 Error().stack
獲取當前正在運行的函數的堆棧跟蹤(stack trace)。
當咱們運行該代碼時,一共作了兩件事: 首先咱們建立一個新的 Error
對象,而後訪問它的 stack
屬性。
獲取堆棧跟蹤的字符串描述信息時,耗費了大量的時間。
我可以經過獲取一個 Error 對象來加快我正在處理的代碼:只有當我須要顯示堆棧跟蹤時,才解析其 stack
屬性。
在個人文章的開頭章節,我提到了很是小的時間間隔可能形成結果的不許確。爲了說明這一點,我在另外一臺不一樣配置的電腦上運行了 Error().stack
的代碼段。
咱們看到了 FormatErrorString
函數,而在以前的分析中,這個函數並無顯示出來。
(此次的總執行時間是 ~1ms,這意味着 Chrome 須要 10 個調用堆棧的樣本。上面的例子花了 ~10ms,由於我在循環中調用了 10 次 Error().stack
。)
若是對 V8 引擎幹興趣,我在 4月15(星期六)晚 8 點和你們一塊兒聊聊 V8 引擎:前端程序員應該懂點 V8 知識 - SegmentFault 講堂。