概述

js的微观性能是指js的某一个表达式或者某一行或者某一块代码的性能。几天前和同事讨论过这方面的内容,今天深入研究了一下,记录下来,供以后开发时参考,相信对其他人也有用。

从一段代码说起

记得以前看关于js的书的时候,书里面不断的强调,在对数组进行循环的时候,要预先缓存数组的长度,不然每次循环都会去拿数组的长度,非常耗时间。比如下面这段代码:

//缓存长度(书上面推荐)
for(var i=0,j=arr.length; i<j; i++){
//do something
} //没有缓存长度
for(var i=0; i<arr.length; i++){
//do something
}

但是有一篇文章:How the Grinch stole array.length access提到一个惊人的结果,没有缓存长度的代码会比缓存长度的代码更快。

原因是:js引擎会对这类代码进行启发式的优化,它会自动缓存数组的长度!所以由于缓存长度的代码会新建一个j变量,这个会带来额外的耗时,所以它执行起来会慢一些。

然后我用三种方法对这段代码进行性能测试。

console.time

代码如下:

//没缓存数组长度
var arr2 = Array(10000).fill(0);
console.time('没缓存');
for(var i2=0; i2<arr2.length; i2++){}
console.timeEnd('没缓存'); //缓存了数组长度
var arr1 = Array(10000).fill(0);
console.time('缓存了');
for(var i1=0,j1=arr1.length; i1<j1; i1++){}
console.timeEnd('缓存了');

经过多次测试,得出的结果是:

  1. 没缓存数组长度的代码执行时间大约是0.15ms
  2. 缓存了数组长度的代码执行时间大约是0.11ms

benchmark.js

可以参考benchmark.js官网。代码如下:

var Benchmark = require('benchmark');
var suite = new Benchmark.Suite; // add tests
suite.add('has not cached', function() {
var arr2 = Array(100).fill(0);
for(var i2=0; i2<arr2.length; i2++){}
})
.add('has cached', function() {
var arr1 = Array(100).fill(0);
for(var i1=0,j1=arr1.length; i1<j1; i1++){}
})
// add listeners
.on('cycle', function(event) {
console.log(String(event.target));
})
.on('complete', function() {
console.log('Fastest is ' + this.filter('fastest').map('name'));
})
// run async
.run({ 'async': true });

得出的结果是(ops/sec越大越快):

  1. 没缓存数组长度的代码的执行速度是3834515 ops/sec。
  2. 缓存了数组长度的代码的执行速度是3668608 ops/sec。

但是当我调整顺序:

var Benchmark = require('benchmark');
var suite = new Benchmark.Suite; // add tests
suite.add('has not cached', function() {
var arr1 = Array(100).fill(0);
for(var i1=0,j1=arr1.length; i1<j1; i1++){}
})
.add('has cached', function() {
var arr2 = Array(100).fill(0);
for(var i2=0; i2<arr2.length; i2++){}
})
// add listeners
.on('cycle', function(event) {
console.log(String(event.target));
})
.on('complete', function() {
console.log('Fastest is ' + this.filter('fastest').map('name'));
})
// run async
.run({ 'async': true });

却得出了相反的结果是:

  1. 没缓存数组长度的代码的执行速度是3433692 ops/sec。
  2. 缓存了数组长度的代码的执行速度是3737960 ops/sec。

jsPerf

jsPerf的链接:https://jsperf.com/

我进行了2个测试,第一个和第二个也是得出了完全相反的结论。

总结

我们看一下上面的三个测试,第一个好像令人信服,但是不能肯定。第二和第三个完全得不出结论,让人感到非常困惑。(所以建议一般都用第一种方法???)

通过这三个测试,我觉得自己好像踩了坑,但是不知道坑在哪里。

通过读《你不知道的JavaScript(中卷)》,我总结了一下微观性能测试会遇到的坑:

  1. js引擎的启发式优化。
  2. 不同的环境结果不一样。
  3. 注意测试的精度。(这就是为什么不用new Date()的原因)
  4. 测试环境运行其它隐藏程序
  5. 不必要的噪音。(创建新的变量?新的匿名函数?)
  6. 额外的工作。(强制类型转换)
  7. 测试不全面。(number? string?)
  8. 不是所有的引擎都类似。

所以要想写好一个好的微观测试用例真的不是那么简单。

然而,计算机界有一句名言:过早优化是万恶之源。意思就是非关键路径的优化是万恶之源。特别对于微观性能来说,如果不是在特别大的循环里面,或者不太常执行的代码,这些微观性能的优化根本没多少用,反而会把我们慢慢的带向无尽的深渊,就像上面我做的三个测试那样,总感觉有坑,但是找不出坑在哪里。

正确的做法应该是先找出影响性能的几个关键瓶颈,然后再针对它们进行优化,这样我们就能保证,一旦优化成功,性能将会上升很大。

js的微观性能的更多相关文章

  1. 使用 Web Tracing Framework 分析富 JS 应用的性能

    来自谷歌的 Web Tracing Framework 包含一组工具和脚本,用于 JavaScript 相关代码的性能分析.它是重 JavaScript 应用程序的理想选择,而 JavaScript ...

  2. 三星a9上测试egret与pixi.js的渲染性能

    for (let i = 0; i < 500; i++) { let shape = new egret.Shape(); shape.graphics.beginFill(0xff0000) ...

  3. 记录:sea.js和require.js配置 与 性能对比

    最近有点忙,很久无写博客,记录一下之前的配置require.js和sea.js的配置.(有误有望提出 require.js 文件目录 /app(项目使用js) /lib(require.js jq存放 ...

  4. 简单对比一下不同Windows操作系统在相同硬件配置的情况下浏览器js引擎的性能

    最近部门进行Windows客户端的测试产品单点性能, 感觉不在通的windows版本以及浏览器内核的情况下性能可能有差异, 也一直没有找到一个比较好的对比工具, 今天用chrome的控制台简单测试了下 ...

  5. 【转】js JavaScript 的性能优化:加载和执行

    JavaScript 的性能优化:加载和执行 转自:https://www.ibm.com/developerworks/cn/web/1308_caiys_jsload/ 随着 Web2.0 技术的 ...

  6. 如何使用 Set 来提高JS代码的性能

    摘要: 高效使用Set! 作者:前端小智 原文:如何使用 Set 来提高代码的性能 Fundebug经授权转载,版权归原作者所有. 为了保证的可读性,本文采用意译而非直译. 我确信有很多开发人员坚持使 ...

  7. js延迟加载的性能优化

    js的延迟加载有助于提高页面的加载速度,特别是竞价优化站是有一定的好处,今天来说说我是如何优化竞价站打开速度! 案例:http://yzmb.pengchenggroup.cn/ 动态创建DOM方式 ...

  8. js循环遍历性能

    定length for循环 (有length) 不定length for循环(使用数组length) 不定length for循环(判断数组length是否存在) forEach(Array自带,对某 ...

  9. three.js学习:性能监视器stats.js的用法

    用法一: var stats = new Stats(); stats.setMode(0); stats.domElement.style.position = 'absolute'; stats. ...

随机推荐

  1. 初试Python语法小试牛刀之冒泡排序

    Python很火,心里很慌,没吃过猪肉,也要见见猪走路. 看了几天Python的语法,大概初步了解了一点点,https://www.liaoxuefeng.com/wiki/0014316089557 ...

  2. 写出良好风格的JS、CSS代码

    现在代码的格式都有 eslint.prettier.babel 这些来保证,但是技术手段再高端都不能解决代码可读性的问题. 因为这个只有个人才能解决.但是注意一下事项,可以显著提高代码的可读性.可识别 ...

  3. 深入浅出PF 学习笔记---资源文件

    引用   xmlns:sys="clr-namespace:System;assembly=mscorlib" <Window.Resources><sys:St ...

  4. [leetcode]8. String to Integer (atoi)字符串转整数

    Implement atoi which converts a string to an integer. The function first discards as many whitespace ...

  5. freemaker超详细 讲解 配置

    一.FreeMarker简介 二.第一个FreeMark示例 2.1.新建一个Maven项目 2.2.添加依赖 2.3.添加存放模板的文件夹 2.4.添加模板 2.5.解析模板 2.6.运行结果 三. ...

  6. 微擎开发------day01

    微擎的数据常量 $_GPC   -- 全局请求变量 类型: array 说明: 合并请求参数, 包括 $_GET, $_POST, $_COOKIE的内容. 相同键名覆盖规则为 $_COOKIE 覆盖 ...

  7. 查看sql 语句io执行情况

    set statistics io,time on 表 'xx'.扫描计数 1,逻辑读取 19 次,物理读取 0 次,预读 0 次,lob 逻辑读取 76 次,lob 物理读取 0 次,lob 预读 ...

  8. CentOS 6.x下wget 下载提示 Unable to locally verify the issuer’s authority 完美解决方案

    CentOS 6.x下wget 下载提示 Unable to locally verify the issuer’s authority 完美解决方案 栏目:Linux 作者:小天 点击: 1,453 ...

  9. 微信小程序设置背景铺满全屏

    参考方法: 新版本升级取消了默认page的100%的特性 需要在app.wxss文件中加入如下代码: page{ height:100%; }

  10. Apache beam中的便携式有状态大数据处理

    Apache beam中的便携式有状态大数据处理 目标: 什么是 apache beam? 状态 计时器 例子&小demo 一.什么是 apache beam? 上面两个图片一个是正面切图,一 ...