我經常在函數中使用可選參數,但是一些測試顯示他們在firefox和safari(70-95%)中有巨大的性能。奇怪的是,如果我通過文字值undefined那麼沒有懲罰。這裏會發生什麼?我不會認爲這是一個範圍鏈問題,因爲它們本身就是本地功能。我是否會開始將undefined轉換爲每個可選參數?未定義參數的性能損失
jsPerf:http://jsperf.com/function-undefined-args/2
我經常在函數中使用可選參數,但是一些測試顯示他們在firefox和safari(70-95%)中有巨大的性能。奇怪的是,如果我通過文字值undefined那麼沒有懲罰。這裏會發生什麼?我不會認爲這是一個範圍鏈問題,因爲它們本身就是本地功能。我是否會開始將undefined轉換爲每個可選參數?未定義參數的性能損失
jsPerf:http://jsperf.com/function-undefined-args/2
對於這樣的功能:
function threeArgs(x, y, z) {
return x + y + z;
}
這就是所謂的像這樣:
threeArgs(1, 2, 3);
優化是自由做出的選擇在所有生成代碼。確定沒有副作用是相當容易的,因爲函數只是引用其參數值並返回簡單表達式的結果。由於返回值被忽略,運行時沒有任何理由做任何事情。
除此之外,如果代碼是:
something += threeArgs(1, 2, 3);
優化器可能會決定生成代碼大致相當於:
something += 6;
爲什麼?由於該調用是使用數字常量進行的,因此可以在代碼生成時安全地摺疊這些調用。這可能是保守的,因爲數字很奇怪,但在這裏它們都是整數,所以它可以做到這一點。即使它沒有,它可以安全地內聯函數:
something += 1 + 2 + 3;
時,有一個參數丟失,但是,它可能是優化保釋出來,產生一個真正的函數調用。對於這樣一個簡單的函數,函數調用的開銷很容易導致性能上的巨大差異。
通過在測試中使用變量而不是常量,並且通過實際使用函數的返回值,您可以「優化」優化器並避免跳過調用或預先計算結果,但您可以'避免內聯。我仍然認爲你的結果是有趣的,因爲這個原因:它暴露了這樣一個事實,即(至今爲止)這些優化器對函數被調用的方式很敏感。
我認爲可以解釋的性能差異是參數傳遞給函數對象的方式:通過arguments
對象。當不傳遞任何參數時,JS將首先掃描任何給定參數的參數對象,當這些參數未定義時,將掃描原型鏈,一直到Object.prototype
。如果這些都缺乏期望的屬性,JS將返回undefined
。然而,經過未定義明確,將其設置爲直接在參數對象的屬性:
function foo(arg)
{
console.log(arguments.hasOwnProperty('0'));
}
foo();//false'
foo('bar');//true
foo(undefined);//true
我推測這就是爲什麼經過未定義明確傾向於更快的原因。
這是一個有趣的想法,但在我看來,由於「arg」符號是在形式參數列表中聲明的,它總是*是一個本地引用。 – Pointy
也許,但將函數改爲'console.log(arg === arguments [0]);'並調用'foo(new Date())'記錄爲真,因此兩者都引用完全相同的東西。我實際上說args是本地'arguments'對象的一個屬性的本地引用。拋開範圍掃描,原型論證仍然成立,不是嗎? –
噢,是的,我同意 - 「參數」對象是奇怪的。我不確定它會如何影響這個,我的意思是。換句話說,因爲「arg」是一個形式參數,所以它的值是* always *參數[0]的值,不管它是不是undefined。 – Pointy
@MattWhipple - 這是一個有趣的帖子,但它與OP的問題有什麼關係? (順便問一下,很好的問題,@robC。) –
這是一個有趣的發現。在更改所有代碼以填充所有參數之前,我會檢查是否存在針對這些JS實現的開放式錯誤報告,如果沒有,則提交文件。 –
我強烈懷疑當有一個參數計數匹配時,運行時正在內聯函數,但在其他情況下將函數作爲實際函數調用運行。沒有任何東西使用函數返回值的事實可能意味着,當完全提供參數時,運行時不*完全*。 – Pointy