Chrome DevTools:在 Profile 性能分析中显示原生 javascript 函数

Stella981
• 阅读 739

本文翻译自 Chrome DevTools: Show native functions in JS Profile

在 Chrome DevTools 中可以使用 profiler 查看原生函数的执行性能:

Chrome DevTools:在 Profile 性能分析中显示原生 javascript 函数

原生函数(native function)是 JavaScript 语言的一部分,这些函数有别于开发者编写的自定义函数。当我们在 profiler 中查看代码的调用栈时,这些函数是被过滤掉的。我们在 profiler 中看到的只有自己写的代码。

当我们捕获调用栈时,Chrome 并不会捕获 C++ 写的函数。不过,在 V8 引擎中很多 javascript 原生函数都是使用 javascript 语言编写的

V8 使用 JavaScript 本身实现了 JavaScript 语言的大部分内置对象和函数。 例如,promise 功能就是通过 JavaScript 编写的。我们把这样的内置函数称为自主托管(self-hosted)。

如果我们开启 “Show native functions” 设置,Chrome 将会在 profiler 中显示这些函数。

Chrome 分析器是如何工作的

为了找到那些耗时最多的代码,Chrome 分析器每 100μs 捕获一个堆栈跟踪。

这意味着,如果一个函数只需要 50μs 的执行时间,就可能不会在分析器中显示出来!

当你分析几毫秒以上的时间时,可以准确了解应用程序在何时花费最多的时间。 但是,当你放大 profiler 面板想看更精准的时间时,信息会变得不太准确。

分析器也会不一致。 每次运行时,会产生一个稍微不同的结果。 有时可能会记录非常短的函数调用,而在其他时间再次运行这些函数调用信息可能会丢失。

通过这篇博客文章我将为大家演示如何捕获并分析原生函数的性能。当你自己运行代码时,结果可能会有所不同。

Array.join

首先,我们运行如下代码:

var arr = []
for (var i=0; i<1000; i++){
    arr.push(i)
}
console.profile("Array.join")
arr.join(",")
console.profileEnd("Array.join")

选择 profiler 的 “Chart” 视图:

Chrome DevTools:在 Profile 性能分析中显示原生 javascript 函数

第一次分析时,我们不选中 “Show native functions”:

Chrome DevTools:在 Profile 性能分析中显示原生 javascript 函数

我们再次运行时,把 “Show native functions” 启用:

Chrome DevTools:在 Profile 性能分析中显示原生 javascript 函数

当我们把鼠标指向函数时,会看到更加详细的信息:

Chrome DevTools:在 Profile 性能分析中显示原生 javascript 函数

如上信息中,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))是非常有趣的。

Chrome DevTools:在 Profile 性能分析中显示原生 javascript 函数

为什么这很有用呢?

下面的代码是如何运行的?

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 DevTools:在 Profile 性能分析中显示原生 javascript 函数

当我们开启了查看原生函数功能时,Chrome 调用了 arraytoString 方法,然后调用了 join 方法。

Chrome DevTools:在 Profile 性能分析中显示原生 javascript 函数

Error().stack

我们来看一个不同的例子。在 JavaScript 中,您可以使用 Error().stack 获取当前正在运行的函数的堆栈跟踪(stack trace)。

当我们运行该代码时,一共做了两件事: 首先我们创建一个新的 Error 对象,然后访问它的 stack 属性。

Chrome DevTools:在 Profile 性能分析中显示原生 javascript 函数

获取堆栈跟踪的字符串描述信息时,耗费了大量的时间。

我能够通过获取一个 Error 对象来加快我正在处理的代码:只有当我需要显示堆栈跟踪时,才解析其 stack 属性。

不准确的地方

在我的文章的开头章节,我提到了非常小的时间间隔可能造成结果的不准确。为了说明这一点,我在另一台不同配置的电脑上运行了 Error().stack 的代码段。

我们看到了 FormatErrorString 函数,而在之前的分析中,这个函数并没有显示出来。

Chrome DevTools:在 Profile 性能分析中显示原生 javascript 函数

(这次的总执行时间是 ~1ms,这意味着 Chrome 需要 10 个调用堆栈的样本。上面的例子花了 ~10ms,因为我在循环中调用了 10 次 Error().stack。)

相关阅读

如果有对 V8 引擎感兴趣的,我会在 4月15(星期六)晚8点和大家一起聊聊 V8 引擎:前端程序员应该懂点 V8 知识 - SegmentFault 讲堂

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Karen110 Karen110
3年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这