本文翻译自 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
。)
这几天 SF 增长了新的板块——直播。我也收到了官方的邀请。4月15(星期六)晚8点和你们一块儿聊聊 V8 引擎:前端程序员应该懂点 V8 知识 - SegmentFault 讲堂。