Javascript性能优化
来源:互联网 发布:八爪鱼三脚架 知乎 编辑:程序博客网 时间:2024/05/21 13:59
JavaScript性能研究
@author huangteng
前言
有关javascript性能的探究,目的只是找出性能提升的最佳途径。
我们执着于性能提升,也就是为了给用户带来最好的体验。
其实一说起性能,大概脑子里第一反应就是同步执行和异步执行。
当然,大家都会选择交替的并发执行(异步)的方式,这样减少阻塞,使得
我们的应用更加流畅。
本次探究我们分为两个层次,一个是程序级别的性能研究,一个是微性能的研究,比如
a++ , ++a 哪个更快?
再次谈谈javascript实现多线程
我们都知道javascript是单线程的,而且目前仍然是这样,不会诞生别的变化。我们也知道,如果能够像多cpu机器一样并行执行多任务,当然是最佳方案。
既然javaScript办不到,我们想想别的呢,HTML5的新特性,Worker,从浏览器实现了多线程的方式运行我们的js代码。其实也就是浏览器可以同时运行多个Js引擎,从而实现多线程式的效果。但是这样的方式又不想我们熟悉的多线程语言,比如java,C++等,多线程之间共享作用域和资源,这样对资源的访问必须依靠锁机制来维持顺序。
Worker建立的多线程并不会共享作用域和资源,线程之间完全依靠事件消息机制相互联系
Web Worker在耗时计算密集型操作中,显得特别实用。在WebWorker中我们可以实现:
1. 可以加载一个JS进行大量的复杂计算而不挂起主进程,并通过postMessage,onmessage进行通信;
2. 可以在worker中通过importScripts(url)加载另外的脚本文件;这些脚本加载是同步的。也就是说,importScripts(..) 调用会阻塞余下 Worker 的执行,直到文件加载和执行完成。
3. 可以使用 setTimeout(),clearTimeout(),setInterval(),clearInterval();
4. 可以使用XMLHttpRequest、WebSockets来发送请求,以及访问navigator、location 、JSON 和 applicationCache 的部分属性。
Worker中的数据传递现在不仅仅只依靠字符串了,感兴趣了解一下结构化克隆数据和
transferrable
有关Worker的用法,如下:
主线程:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>main process</title></head><body> <div> <h1>这是主线程中的页面</h1> </div> <div style=" width: 500px ;height:500px ;background: #e8e8e8"> <div style="width: 500px ;height:200px ;background: #e8e8e8"> <h3 id="result"></h3> </div> <div style="width: 500px ; height: 100px ;position: absolute; bottom: 0px"> <button style="width: 80px ;height:30px;border-radius: 5px" onClick="start()">startWorker</button> <button style="width: 80px ;height:30px;border-radius: 5px" onClick="end()">endWorker</button> </div> </div></body><script type="text/javascript"> // 利用python启动一个简单的服务 // 建立一个worker,参数是一个js文件路径 // 工作线程里面一般是很耗cpu的操作,这里的demo简单的实现主线程和工作线程之间的通话。 var w1; function start(){ if(typeof(Worker) !== 'undefined'){ if(typeof(w1) === 'undefined'){ w1 = new Worker('/worker1.js'); } // 建立与工作线程的通信;postMessgae-发送信息;onmessage-监听接收信息;在工作线程里面是相对的操作。 var i = 100; w1.postMessage(i); w1.onmessage = function(evt){ document.getElementById('result').innerHTML=evt.data; } }else{ document.getElementById('result').innerHTML = 'current browser not support worker!'; } } // 停止工作线程 function end(){ w1.terminate() }</script></html>
工作线程 work.js:
(function(){ console.log('worker: ','<h4>这是一个Worker里的</h4>') var rep = 0 ; addEventListener('message',function(evt){ rep = evt.data + 200; }) setTimeout(function(){ postMessage('给你的哦!'+ rep); },2000)})()
更多内容比如sharedWorker,就是几个页面共享一个工作线程。
假如下面的页面有多个
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>main2</title></head><body> <input type="text" style="width: 200px;height:25px;border-radius: 5px;background: #e8e8e8" id="text" /><input type="button" onClick="start()" style="margin-left:30px;width: 50px;height:25px;border-radius: 5px;background: #e8e8e8" value="send"/> <script type="text/javascript"> (function callee(){ var itv=setTimeout(function(){ console.log("timeout!"); callee(); },1000); // 这里主要验证一下sharedWorker // 方法就简单写了,主要说明sharedWorker的消息传递 // 共享worker就是几个页面或者tab共享一个worker,他们之间的消息传递就需要通过port来区别开来 // 这里的demo通过一个页面的输入在另一个页面显示,中转的地方是sharedworker来演示 new SharedWorker('sharedworker.js').port.onmessage = function(evt){ if(evt.data.name=="connected") clearTimeout(itv),console.log("connected~"); } console.log("connecting...") })() </script></body></html>
共享线程,sharedworker.js:
onconnect=function(e){ e.ports[0].postMessage({name:"connected"});};
可以参考:https://www.ibm.com/developerworks/cn/web/1112_sunch_webworker/
性能测试与调优
上述部分是针对程序级别的宏观的性能研究,其实这完全依赖与Ecma标准的发展,未来不排除激进的让javaScript变成多线程的语言。
本节的内容主要针对的是微性能的探究。
代码测试
js中对一段代码测试,大多数人采用的方法是这样的
var start = Date.now();// 这里是你的测试片段...var end = Date.now();console.log('run time',end-start);
这样的测试基本是无效的,你要考虑平台精度,方法执行的延误,样本的数量,最大最小的离散偏差…
测试应该是统计学的结果。我们不擅长无所谓,可以借助第三方工具,比如Benchmark.js
、jsPerf等…
这里以Benchmark.js的使用为案例,写一个测试demo,有兴趣的可以自己学习一下上面说的两种测试工具的使用。
/* * @author: * @description: 测试 "+" 和 join性能 * @version: * @time: */ const Benchmark = require('benchmark') const suite = new Benchmark.Suite; var test1 = ()=>{ var res = ''; var num = 100000; while(num--){ res += '<a></a>' } } var test2 = ()=>{ var arr = []; var num = 100000; while(num--){ arr.push('<a></a>') } arr = arr.join(''); } suite.add('+ test',function(){ test1(); }).add('join test',function(){ test2(); }).on('cycle',function(evt){ console.log(String(evt.target)) }).on('complete',function(){ console.log('Fastest is '+ this.filter('fastest').map('name')) }).run({'async':true})
运行结果:
+ test x 583 ops/sec ±8.44% (51 runs sampled)join test x 184 ops/sec ±8.36% (55 runs sampled)Fastest is + test[Finished in 25.5s]
说明在100000次简单拼接的时候,字符串”+”拼接更快
其中ops/sec代表每秒执行次数,这个值越大说明性能越好
后面的XXX%代表系统误差。
benchmark的缺陷就是不能代表所有的环境,假如你在IE上测试出X性能 > Y
不代表在chrome上就会成立。
为了解决这一问题,jsPerf.com,这个网站诞生了,他其实也是run的是benchmark,
只是支持了不同的浏览器环境,还可以在线分享,累积测试,浏览器性能测试等。
至于jsPerf做测试以及在线分享,有兴趣的可以自己去学习一下。
这是我jsPerf的测试结果:https://jsperf.com/huangtengTest123
你可以把这个地址在不同浏览器打开测试性能。
现在你可以尽情的测试你以前写的代码了,也可以解答你的困惑,++a,a++到底谁更快,
我要提醒的是,不要沉迷于代码性能提升,要环境为主,人的感知,比如肉眼能感觉到的极限应该是100ms左右,也就是你的程序性能低于这个值当然牛逼,再继续提升性能就没有多大实际意义,因为反正人类已经感觉不出来了。
微性能
针对具体到某一段代码的优化,我是不可能举出所有列子的,在开发中应该注重代码测试,选择最好的性能代码。但是不要过度的执迷于代码的性能追求。
你写的代码在运行的时候,经过编译的几个步骤,打散成单元,重构AST,运行,可能就不是你写的那个样子了,所以探究是为了实际开发,不要钻牛角尖。
比如前面提到的类似的 –a 和 a– , 在汇编的层次,–a 是首选。但是你实际测试一下,会觉得差别微乎其微。
再比如一个字符串”42”转成数字类型再/2,有很多方法:
parseInt('42')/2;Number('42')/2;+'42'/2;('42'|0)/2'42'/2;...
哪一个更快?
其实这要结合实际,综合考量,比如 +’42’的方式快于parseInt,但是可读性差。
我们应该关注优化的大局,而不是担心这些微观性能的细微差别。
这里补充一个ES6的优化细节。CTO(“尾调用优化”)
就是指在一个函数(假设叫bar)的尾部调用某个函数(假设叫foo),而foo的尾部不会再调用其它函数了,而是直接返回一个结果或者别的。
ES6要求引擎实现了CTO,这样的好处是尾调用不会在bar里创建栈帧,这就节省了内存开销。
虽然看似没什么广泛的用途,但是在递归里面,你可以脑补,简直美如画啊。
有关js性能优化就到这里。
- 关于javascript性能优化
- javascript性能优化
- javascript 性能优化
- javascript 性能优化
- 浅谈 Javascript 性能优化
- javascript性能优化
- javascript 性能优化
- Javascript性能优化
- javascript性能优化小结
- JavaScript性能优化总结
- JavaScript性能优化
- javascript性能优化
- JavaScript性能优化
- javascript性能优化
- Javascript 加载性能优化
- javascript性能优化
- javascript 性能优化汇总
- javaScript的性能优化
- Android 获取电池信息
- 系列:iOS开发-CocoaPods的安装和使用
- 使用github的使用,利用git shell命令行模式进行操作
- 理解拉格朗日乘子法和KKT条件
- url通配符
- Javascript性能优化
- ES6 学习笔记 Class
- ROS系统学习第一课
- webstorm 使用svn
- 【XML解析】使用Dom4j对XML进行SAX解析
- 像素旋转
- 如何在图片中加入噪点骗过Google最顶尖的图像识别AI
- Set是如何不能加入重复值
- HTML5对手机页面长按会粘贴复制禁用的解决方法(常用)