2012-09-30 80 views
10

我經常在函數中使用可選參數,但是一些測試顯示他們在firefox和safari(70-95%)中有巨大的性能。奇怪的是,如果我通過文字值undefined那麼沒有懲罰。這裏會發生什麼?我不會認爲這是一個範圍鏈問題,因爲它們本身就是本地功能。我是否會開始將undefined轉換爲每個可選參數?未定義參數的性能損失

jsPerf:http://jsperf.com/function-undefined-args/2

+5

@MattWhipple - 這是一個有趣的帖子,但它與OP的問題有什麼關係? (順便問一下,很好的問題,@robC。) –

+0

這是一個有趣的發現。在更改所有代碼以填充所有參數之前,我會檢查是否存在針對這些JS實現的開放式錯誤報告,如果沒有,則提交文件。 –

+0

我強烈懷疑當有一個參數計數匹配時,運行時正在內聯函數,但在其他情況下將函數作爲實際函數調用運行。沒有任何東西使用函數返回值的事實可能意味着,當完全提供參數時,運行時不*完全*。 – Pointy

回答

5

對於這樣的功能:

function threeArgs(x, y, z) { 
    return x + y + z; 
} 

這就是所謂的像這樣:

threeArgs(1, 2, 3); 

優化是自由做出的選擇在所有生成代碼。確定沒有副作用是相當容易的,因爲函數只是引用其參數值並返回簡單表達式的結果。由於返回值被忽略,運行時沒有任何理由做任何事情。

除此之外,如果代碼是:

something += threeArgs(1, 2, 3); 

優化器可能會決定生成代碼大致相當於:

something += 6; 

爲什麼?由於該調用是使用數字常量進行的,因此可以在代碼生成時安全地摺疊這些調用。這可能是保守的,因爲數字很奇怪,但在這裏它們都是整數,所以它可以做到這一點。即使它沒有,它可以安全地內聯函數:

something += 1 + 2 + 3; 

時,有一個參數丟失,但是,它可能是優化保釋出來,產生一個真正的函數調用。對於這樣一個簡單的函數,函數調用的開銷很容易導致性能上的巨大差異。

通過在測試中使用變量而不是常量,並且通過實際使用函數的返回值,您可以「優化」優化器並避免跳過調用或預先計算結果,但您可以'避免內聯。我仍然認爲你的結果是有趣的,因爲這個原因:它暴露了這樣一個事實,即(至今爲止)這些優化器對函數被調用的方式很敏感。

+2

這似乎是一個似是而非的解釋,但我們怎麼知道這真的是這樣發生的? – jfriend00

+0

FF和最新版Chrome的結果遠遠領先於該領域,因此它只能真正進行優化。帶有懲罰的函數調用實際上與其他非優化的瀏覽器具有相同的性能。 – robC

+0

「但是,如果缺少某個參數,則可能是優化者紓困」 - 您能詳細說明原因嗎?如果缺少一個參數,它肯定是未定義的,所以對我來說,它似乎只是內聯「1 + 2 + undefined」。 – pimvdb

2

我認爲可以解釋的性能差異是參數傳遞給函數對象的方式:通過arguments對象。當不傳遞任何參數時,JS將首先掃描任何給定參數的參數對象,當這些參數未定義時,將掃描原型鏈,一直到Object.prototype。如果這些都缺乏期望的屬性,JS將返回undefined。然而,經過未定義明確,將其設置爲直接在參數對象的屬性:

function foo(arg) 
{ 
    console.log(arguments.hasOwnProperty('0')); 
} 
foo();//false' 
foo('bar');//true 
foo(undefined);//true 

我推測這就是爲什麼經過未定義明確傾向於更快的原因。

+4

這是一個有趣的想法,但在我看來,由於「arg」符號是在形式參數列表中聲明的,它總是*是一個本地引用。 – Pointy

+0

也許,但將函數改爲'console.log(arg === arguments [0]);'並調用'foo(new Date())'記錄爲真,因此兩者都引用完全相同的東西。我實際上說args是本地'arguments'對象的一個​​屬性的本地引用。拋開範圍掃描,原型論證仍然成立,不是嗎? –

+2

噢,是的,我同意 - 「參數」對象是奇怪的。我不確定它會如何影響這個,我的意思是。換句話說,因爲「arg」是一個形式參數,所以它的值是* always *參數[0]的值,不管它是不是undefined。 – Pointy