2014-09-13 152 views
5

閱讀文檔時,我發現了一個簡單的優化,大大提高了JavaScript的性能。Javascript功能優化(`)`

原始代碼:

function parseRow(columns, parser) { 
    var row = {}; 
    for (var i = 0; i < columns.length; i++) { 
    row[columns[i].name] = parser.readColumnValue(); 
    } 
} 

優化代碼:

var code = 'return {\n'; 
columns.forEach(function(column) { 
    code += '"' + column.name + '":' + 'parser.readColumnValue(),\n'; 
}); 
code += '};\n'; 

var parseRow = new Function('columns', 'parser', code); 

這裏找到:https://github.com/felixge/faster-than-c
爲什麼它運行快20%?
我相信它會刪除for語句,但不會有forEach具有相同的計算成本?

+0

它是否在談論20%的功能創建邏輯本身?或者每次調用函數parseRow?如果是後一種情況,我會假設增益發生是因爲優化代碼中沒有for循環。 (在第一種情況下,for循環會在每次調用parseRow函數時運行,並且多次調用columns.length屬性也可能歸因於原始代碼的緩慢性,只是我的2美分:)) – 2014-09-13 13:10:51

+1

相關(對於V8)http ://www.youtube.com/watch?v = UJPdhx5zTaw – 2014-09-13 13:21:30

+1

當'columns'的大小足夠大時,'優化後的代碼'似乎沒有更快。請參閱[jsPerf](http://jsperf.com/javascript-optimization-with-new-function) – rhgb 2014-09-13 14:02:48

回答

5

不同之處在於您只使用forEach構造的優化函數。創建函數後,內部沒有任何循環:loop is unrolled和列名稱爲hardcoded。然後將該方法編輯爲一個工作函數,該函數甚至可以編譯爲機器代碼depending on the engine。這將導致兩個性能方面的改進:

  1. 通過消除for循環條件檢查(i < columns.length)完全,沒有分支,並
  2. 通過硬編碼的column[i].name值轉換成多個語句,你刪除評估column[i]和查找到column.name在每一步。

所以調用new Function(...)與作爲String通過代碼後,您parseRow變量得到參照以下功能:

function parseRow(columns, parser) { 
    return { 
     "columnOne": parser.readColumnValue(), 
     "columnTwo": parser.readColumnValue(), 
     "columnThree": parser.readColumnValue(), 
     ... 
    }; 
} 

注意,沒有任何的循環,分支,或其他查詢在該代碼中,除了多個parser.readColumnValue()調用。

這爲什麼在JavaScript中可能?

在JavaScript中如此高效地工作的原因是因爲任何網頁中的JavaScript源代碼無論如何都需要由JS引擎解釋或編譯。你不會發布你的網頁編譯的可執行文件,甚至(有些)預編譯的字節碼(如Java或.NET)。每載入一個新的.js文件,瀏覽器在運行之前都會從頭開始編譯它(呃,確切地說,在現代引擎中,它是解釋和編譯之間的東西,即JITting)。

這意味着在運行時從字符串(即編譯代碼)創建工作函數的效率並不比從手寫代碼讀取.js文件低。與C/C++程序比較,該程序編譯爲機器碼(即可執行文件儘可能靠近CPU)before it reaches the customer(在所有合理的情況下)。

如果你想在C++(一種self-modifying code)中做到這一點,你將不得不捆綁一個編譯器沿着你的應用程序來構建代碼,並且構建這個函數的代價會超過你獲得的好處你最終會開始它。例如,在.NET中,對於程序emit methods or even assemblies at run time而言,這並不罕見,然後它將JIT編譯爲機器代碼,以允許潛在的性能改進,例如您的問題。

2

性能提升取決於JavaScript引擎以及正在處理的數據。我們不知道「快20%」的確切情況(除了使用node.js)。在某些情況下它可能會變慢。 (編輯:您需要經常調用該功能足以超過建設成本)。增益的一些可能原因:

優化後的代碼會創建一個對象字面值。以前的版本不斷地將值分配給不存在的屬性。這有一些相關的成本。

row[columns[i].name]有三個查找,而優化版本沒有,一旦函數被構造。不要忘記,row[columns[i].name]尚不存在,所以查找更昂貴。 columns.length也是查找。