Javascript學(xué)習(xí)筆記-程序性能和性能測試

Javascript性能和性能測試.png

這次的內(nèi)容太多沒有研究過了,只用過很少的一部分,所以僅僅作為初步的了解來進行總結(jié)。

1. 性能提升

1.1 WebWorker

WebWorker是HTML5提供的方案,是基于多線程的Javascript。如我們所知,Javascript是單線程運行的,自身并不具備多線程能力,但是Javascript的宿主環(huán)境,卻可以包含多個Javascript引擎,多個Javascript引擎的并行執(zhí)行,就可以實現(xiàn)多線程的能力。一般情況下會有一個主UI線程以及一個或多個Worker線程。
但是多線程運行可能導(dǎo)致數(shù)據(jù)共享會存在問題,所以WebWorker中主UI線程的數(shù)據(jù)是不能和Worker線程直接進行共享,他們之間的數(shù)據(jù)傳遞的方式進行數(shù)據(jù)溝通。

1.1.1 Worker

用一個例子來說明如何創(chuàng)建和使用一個Worker

main.js 
// 創(chuàng)建一個Worker
var w1 = new Worker('w1.js');
// 添加message事件監(jiān)聽
w1.addEventListener('message', function(evt){
  // 獲取到子線程處理后返回的結(jié)果
})
// 發(fā)送數(shù)據(jù)到Worker中
w1.postMessage('patrick');

w1.js
// 添加message事件監(jiān)聽
addEventListener('message', function(evt){
  // 獲取主線程中post的數(shù)據(jù),這里可以理解為拿到'patrick'
  // 調(diào)用方法處理數(shù)據(jù)后使用postMessage返回給主線程
  postMessage('Hello, ' + evt.data);
})

Worker.js中可以通過importScripts()引入第三方的js文件,只是導(dǎo)入過程是同步阻塞的。
使用worker.terminate()方法可以終止worker線程

1.1.2 SharedWorker

使用Worker創(chuàng)建的子線程,在不同的瀏覽器tab中并不是公用的。每一個tab都會獨立創(chuàng)建一個子線程,子線程中的變量在不同tab間并不通用,可以使用SharedWorker來使子線程在多個tab間的數(shù)據(jù)共享

 main.js 
// 創(chuàng)建一個Worker
var w2 = new SharedWorker('w2.js');
// 添加message事件監(jiān)聽,但是是綁定在port屬性上
w2.port.addEventListener('message', function(evt){
  // 獲取到子線程處理后返回的結(jié)果
})
// 發(fā)送數(shù)據(jù)到Worker中,也需要綁定在port屬性上
w2.port.postMessage('patrick');
// 開啟port
w2.port.start();

w2.js
var i = 0;
// 增加connect事件監(jiān)聽
addEventListener('connect', function(evt) {
  // 獲取到port信息
  var port = evt.ports[0]
  // 添加message事件監(jiān)聽
  port.addEventListener('message', function(evt){
    // 變量i在每一個tab中共享
    port.postMessage(`Hello, ${evt.data} ${i} times`);
  })
  // 啟動port
  port.start()
})

相比于普通的Worker,SharedWorker需要綁定到某個port,并對port進行事件監(jiān)聽,同時調(diào)用port.start()啟動port
Web Worker要在Server上運行,否則Chrome會報錯給予警告,雖然網(wǎng)上有說Safari可以不用運行在Server環(huán)境中,實際嘗試過也無法運行

1.2 SIMD

SIMD(Single Instruction/Multiple Data) 單結(jié)構(gòu)多數(shù)據(jù),是一種放棄掉多線程,直接利用CPU提供的API進行位運算的一種提案,目前根據(jù)MDN網(wǎng)上的說法,這個提案已經(jīng)被廢棄,被WebAssembly取代,有興趣的可移步MDN SIMD

1.3 asm.js

一種Javascript的編程規(guī)范,主要是通過代碼規(guī)范,創(chuàng)建數(shù)據(jù)操作緩存堆,減少Javascript引擎進行的多余的操作,例如強制類型轉(zhuǎn)換,垃圾回收等,從而提高Javascript性能,可以通過插件來將現(xiàn)有js代碼進行轉(zhuǎn)換,具體信息請移步MDN asm.js

1.4 WebAssembly

Javascript中引用其他類型語言并執(zhí)行的解決方案,使用相關(guān)的API,可以在Javascript中引入C/C++等其他類型語言執(zhí)行,并不影響現(xiàn)有Javascript代碼,是目前嘗試性質(zhì)的解決方案MDN WebAssembly
關(guān)于WebAssembly會在自己學(xué)習(xí)研究之后專門寫一篇總結(jié),歡迎持續(xù)關(guān)注。

以上的1.1-1.4方案實際上均是在利用Javascript處理一些耗時或者較大數(shù)據(jù)量大問題:大數(shù)據(jù)分析,圖形解析時提供的可參考方案

1.5 尾調(diào)用

尾調(diào)用是指函數(shù)運行的最后是一個函數(shù)調(diào)用,在ES6規(guī)范中需要各Javascript引擎強制去實現(xiàn)相關(guān)的優(yōu)化。尾調(diào)用是屬于函數(shù)式編式編程的范疇。

// 尾調(diào)用
function f1() {
  return f1();
}
// 非尾調(diào)用
function f2() {
  return f2() + 1;
}

Javascript引擎在運行過程中,會為每一個執(zhí)行的函數(shù)創(chuàng)建一個棧幀,而對于尾調(diào)用來說,由于上一個函數(shù)已經(jīng)結(jié)束,沒有其他任何操作,所以當(dāng)前函數(shù)的棧幀會自動分配給尾調(diào)用的函數(shù),不會去創(chuàng)建新的棧幀,減小了內(nèi)存的消耗,尤其是在我們使用遞歸的時候,避免遞歸帶來的內(nèi)存溢出,使用尾調(diào)用的遞歸也叫做尾遞歸。
更詳細的了解可以參考阮一峰大神的尾調(diào)調(diào)用優(yōu)化,里面有更詳細的圖文描述和擴展。

2. 性能測試

2.1 雜談

我們當(dāng)然是希望代碼性能越優(yōu)秀越好,同樣的操作耗時越少,肯定方案是更好的,從表面上看這一點毋庸置疑,但是很多時候?qū)ψ顑?yōu)代碼追求的時候需要考慮很多因素,并不是所有的時候都要去追求最佳性能

  1. Javascript宿主環(huán)境不同,導(dǎo)致Javascript引擎對同一段代碼的優(yōu)化規(guī)則存在差異,所以不同環(huán)境下,同一段代碼并不能始終保持最優(yōu)
  2. 對于微觀代碼0.1ms和0.01ms的之間的導(dǎo)致的性能差距可能并不明顯,所以拘泥于微觀性能并沒有太多用處
  3. 對于性能的優(yōu)化和評定最好基于一個比較大的上下文中進行,這樣的優(yōu)化結(jié)果才更有意義

2.2 benchmark.js

通常我們?nèi)绻容^兩段代碼之間的性能,會在每段代碼運行前添加獲取一個時間startTime,在代碼運行結(jié)束后再獲取一個時間endTime,然后兩者進行差值,再比較兩段代碼之間的差值,從而得出哪一段性能更佳

var startTime = Date.now();
// Todo 一些邏輯
var endTime = Date.now();
console.log(startTime - endTime);

這樣的做法表面上來說是沒有問題的,但是考慮的因素太少,一方面由于獲取時間的過程存在消耗和精度問題,可能會影響測試結(jié)果;另一方面單次執(zhí)行結(jié)果并不能很好的說明一段代碼比另一段性能更好,可能多次運行之后結(jié)果會存在不同,雖然可以考慮添加循環(huán)來執(zhí)行多次,并使用平均值來獲取平均時間判斷,但是在統(tǒng)計學(xué)上這樣結(jié)果的可信度也不夠高。
很慶幸有大牛深諳統(tǒng)計學(xué)知識,并幫助我們創(chuàng)建了一套可信的性能統(tǒng)計代碼來幫助我們進行性能比較,也就是benchmark.js,官方有很詳細的介紹。

2.3 jsPerf.com

基于benchmark.js提供了多宿主環(huán)境,多條件下的執(zhí)行比較,相關(guān)資料也可以通過訪問官方網(wǎng)站jsPerf.com

3. 參考

《你不知道的Javascript(中卷)》
使用benchmark.js和jsPerf.com進行性能分析
阮一峰——尾調(diào)用優(yōu)化

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容