最近在作遊戲引擎性能優化,關於js執行性能有些內容拿來這裏分享。c++
首先須要明確兩點:設計模式
第一,本文討論的js性能問題,都是在大量執行的狀況下才暴露出來的。通常來講,60fps的遊戲,若是每幀須要執行2000次以上,那麼就能夠考慮本文的優化思路了。若是執行頻次沒有達到以上量級,性能並不會有明顯提高。數組
第二,獲得的這些性能紅利在某些狀況下須要犧牲代碼結構與可讀性,考慮在實際項目中是否值得。不少時候,須要犧牲一點點性能來使你的代碼更容易維護。切忌對項目過分優化。緩存
如下幾點,已經證實在大量執行的狀況下,會對項目性能產生影響。性能優化
設計模式和重構原則常常告訴咱們:bash
一個函數只作一件事,若是幾個函數都在作一件事,那麼抽象出一個對象架構
是的,這些原則在業務邏輯開發中頗有用,讓代碼更加容易維護,同時對性能幾乎沒有什麼影響。可是,某些狀況下它並非毫無性能開銷的。當執行太多的函數調用後,你會發現性能變得很慢。每當我減小一些函數調用(把函數中的代碼直接拷貝到當前調用位置),性能都會有提高。app
例如,在渲染引擎中常常會有矩陣計算,咱們通常會抽象出一個matrix類:ide
matrix1.append(matrix2)
複製代碼
來實現矩陣相乘,但若是這段代碼是在主渲染循環中,你可能就要考慮展開成以下的樣子:函數
matrix1.a = matrix2.a * matrix1.a + matrix2.b * matrix1.c;
// ...
複製代碼
這樣性能真的會有一些提高(大量執行的狀況下),若是你的函數調用路徑過長,建議進行一些流程簡化。
除非極特殊的狀況,要從架構上減小函數調用,不要粗暴地拆散函數。
js在查找變量的時候,會從當前做用域開始依次向上層查找。所以能夠推斷出,局部變量的訪問永遠是最快的,全局變量的訪問永遠是最慢的。
以下例:
var array = [];
function getName() {
array[0] = 0;
array[1] = 0;
array[2] = 0;
array[3] = 0;
// ...
}
複製代碼
性能會低於:
var array = [];
function getName() {
var array = array;
array[0] = 0;
array[1] = 0;
array[2] = 0;
array[3] = 0;
// ...
}
複製代碼
若是在一個函數中屢次調用全局變量或外層變量,記得先把它保存到局部變量。
屢次調用this關鍵字會讓你的js執行很慢。由於當你訪問一個對象屬性,js執行時就要去查找原型鏈,直到查找到該屬性爲止。這個開銷是很大的。
下面的例子:
for(var i = 0; i < 100; i++) {
this.array[i] = i;
}
複製代碼
性能低於:
var array = this.array;
for(var i = 0; i < 100; i++) {
array[i] = i;
}
複製代碼
若是屢次訪問對象屬性(甚至循環調用),建議先把該屬性保存到一個局部變量中,再使用。
js的四則運算中,除法是最慢的,乘法其次。Math封裝的數學函數中,sin與cos函數執行是最慢的。
下面的例子:
// a在大部分狀況下爲0
c = a * b;
f = a * e;
複製代碼
性能低於:
// a在大部分狀況下爲0
if(a == 0) {
c = 0;
f = 0;
} else {
c = a * b;
f = a * e;
}
複製代碼
儘可能避免沒必要要的乘除運算,可能的狀況下,緩存sin和cos運算結果。pixi.js中,顯示對象的旋轉要用到三角函數計算,引擎內部進行了標髒處理。egret中,對全局的三角函數計算方法進行了查表優化
。
在主循環方法中仔細查找,項目中可能存在不少相似的可優化點。
大量調用數組push與pop方法,若是這些調用出如今循環中,那麼很不幸,它會形成性能的降低。
若是在你的渲染循環中有這樣的結構:
var matrix = Matrix.create();
// do something
Matrix.release(matrix);
複製代碼
而且對象池是這樣實現的:
// 建立matrix對象
Matrix.create = function() {
return Matrix.pool.pop() || new Matrix();
}
// 釋放matrix對象
Matrix.release = function(matrix) {
Matrix.pool.push(matrix);
}
複製代碼
那麼你的主循環會被push與pop拖慢速度。
通常來講,引擎中會大量使用的臨時對象。特定狀況下,對於臨時對象,除了使用對象池,還能夠用一個全局的temp對象替代:
例如,上例中,咱們能夠定義一個用於引擎內部臨時使用的matrix對象 $tempMatrix:
Matrix.$tempMatrix = new Matrix();
複製代碼
使用的時候只須要:
var matrix = Matrix.$tempMatrix;
// do something
matrix.identify();
複製代碼
細心的同窗可能會說,這樣重複使用一個對象,若是代碼邏輯上須要多個temp對象,上面的實現就不能解決需求了。的確,那咱們就須要用其它的方式來解決。不過對於特定狀況,上面這種優化是簡單有效的。
總結來看,這些所謂的性能優化點,大部分都是js語言在運行過程當中的弱點,在其它語言中未必會重現。例如,上文提到的函數調用,在c++等語言中並不會對性能形成明顯影響。
另外,若是你們作的不是相似於引擎這樣的底層產品的話,這些東西瞭解一下也就得了。
再次強調,永遠不要過分優化!!!