當我在JavaScript中實現ChaCha20時,我偶然發現了一些奇怪的行爲。奇怪的JavaScript性能
我的第一個版本是建立這樣的(我們稱之爲「封裝版本」):
function quarterRound(x, a, b, c, d) {
x[a] += x[b]; x[d] = ((x[d]^x[a]) << 16) | ((x[d]^x[a]) >>> 16);
x[c] += x[d]; x[b] = ((x[b]^x[c]) << 12) | ((x[b]^x[c]) >>> 20);
x[a] += x[b]; x[d] = ((x[d]^x[a]) << 8) | ((x[d]^x[a]) >>> 24);
x[c] += x[d]; x[b] = ((x[b]^x[c]) << 7) | ((x[b]^x[c]) >>> 25);
}
function getBlock(buffer) {
var x = new Uint32Array(16);
for (var i = 16; i--;) x[i] = input[i];
for (var i = 20; i > 0; i -= 2) {
quarterRound(x, 0, 4, 8,12);
quarterRound(x, 1, 5, 9,13);
quarterRound(x, 2, 6,10,14);
quarterRound(x, 3, 7,11,15);
quarterRound(x, 0, 5,10,15);
quarterRound(x, 1, 6,11,12);
quarterRound(x, 2, 7, 8,13);
quarterRound(x, 3, 4, 9,14);
}
for (i = 16; i--;) x[i] += input[i];
for (i = 16; i--;) U32TO8_LE(buffer, 4 * i, x[i]);
input[12]++;
return buffer;
}
要(開銷參數等),減少不必要的函數調用我刪除了quarterRound
- 函數,並把它的內容內聯(這是正確的,我驗證了其對一些測試向量):
function getBlock(buffer) {
var x = new Uint32Array(16);
for (var i = 16; i--;) x[i] = input[i];
for (var i = 20; i > 0; i -= 2) {
x[ 0] += x[ 4]; x[12] = ((x[12]^x[ 0]) << 16) | ((x[12]^x[ 0]) >>> 16);
x[ 8] += x[12]; x[ 4] = ((x[ 4]^x[ 8]) << 12) | ((x[ 4]^x[ 8]) >>> 20);
x[ 0] += x[ 4]; x[12] = ((x[12]^x[ 0]) << 8) | ((x[12]^x[ 0]) >>> 24);
x[ 8] += x[12]; x[ 4] = ((x[ 4]^x[ 8]) << 7) | ((x[ 4]^x[ 8]) >>> 25);
x[ 1] += x[ 5]; x[13] = ((x[13]^x[ 1]) << 16) | ((x[13]^x[ 1]) >>> 16);
x[ 9] += x[13]; x[ 5] = ((x[ 5]^x[ 9]) << 12) | ((x[ 5]^x[ 9]) >>> 20);
x[ 1] += x[ 5]; x[13] = ((x[13]^x[ 1]) << 8) | ((x[13]^x[ 1]) >>> 24);
x[ 9] += x[13]; x[ 5] = ((x[ 5]^x[ 9]) << 7) | ((x[ 5]^x[ 9]) >>> 25);
x[ 2] += x[ 6]; x[14] = ((x[14]^x[ 2]) << 16) | ((x[14]^x[ 2]) >>> 16);
x[10] += x[14]; x[ 6] = ((x[ 6]^x[10]) << 12) | ((x[ 6]^x[10]) >>> 20);
x[ 2] += x[ 6]; x[14] = ((x[14]^x[ 2]) << 8) | ((x[14]^x[ 2]) >>> 24);
x[10] += x[14]; x[ 6] = ((x[ 6]^x[10]) << 7) | ((x[ 6]^x[10]) >>> 25);
x[ 3] += x[ 7]; x[15] = ((x[15]^x[ 3]) << 16) | ((x[15]^x[ 3]) >>> 16);
x[11] += x[15]; x[ 7] = ((x[ 7]^x[11]) << 12) | ((x[ 7]^x[11]) >>> 20);
x[ 3] += x[ 7]; x[15] = ((x[15]^x[ 3]) << 8) | ((x[15]^x[ 3]) >>> 24);
x[11] += x[15]; x[ 7] = ((x[ 7]^x[11]) << 7) | ((x[ 7]^x[11]) >>> 25);
x[ 0] += x[ 5]; x[15] = ((x[15]^x[ 0]) << 16) | ((x[15]^x[ 0]) >>> 16);
x[10] += x[15]; x[ 5] = ((x[ 5]^x[10]) << 12) | ((x[ 5]^x[10]) >>> 20);
x[ 0] += x[ 5]; x[15] = ((x[15]^x[ 0]) << 8) | ((x[15]^x[ 0]) >>> 24);
x[10] += x[15]; x[ 5] = ((x[ 5]^x[10]) << 7) | ((x[ 5]^x[10]) >>> 25);
x[ 1] += x[ 6]; x[12] = ((x[12]^x[ 1]) << 16) | ((x[12]^x[ 1]) >>> 16);
x[11] += x[12]; x[ 6] = ((x[ 6]^x[11]) << 12) | ((x[ 6]^x[11]) >>> 20);
x[ 1] += x[ 6]; x[12] = ((x[12]^x[ 1]) << 8) | ((x[12]^x[ 1]) >>> 24);
x[11] += x[12]; x[ 6] = ((x[ 6]^x[11]) << 7) | ((x[ 6]^x[11]) >>> 25);
x[ 2] += x[ 7]; x[13] = ((x[13]^x[ 2]) << 16) | ((x[13]^x[ 2]) >>> 16);
x[ 8] += x[13]; x[ 7] = ((x[ 7]^x[ 8]) << 12) | ((x[ 7]^x[ 8]) >>> 20);
x[ 2] += x[ 7]; x[13] = ((x[13]^x[ 2]) << 8) | ((x[13]^x[ 2]) >>> 24);
x[ 8] += x[13]; x[ 7] = ((x[ 7]^x[ 8]) << 7) | ((x[ 7]^x[ 8]) >>> 25);
x[ 3] += x[ 4]; x[14] = ((x[14]^x[ 3]) << 16) | ((x[14]^x[ 3]) >>> 16);
x[ 9] += x[14]; x[ 4] = ((x[ 4]^x[ 9]) << 12) | ((x[ 4]^x[ 9]) >>> 20);
x[ 3] += x[ 4]; x[14] = ((x[14]^x[ 3]) << 8) | ((x[14]^x[ 3]) >>> 24);
x[ 9] += x[14]; x[ 4] = ((x[ 4]^x[ 9]) << 7) | ((x[ 4]^x[ 9]) >>> 25);
}
for (i = 16; i--;) x[i] += input[i];
for (i = 16; i--;) U32TO8_LE(buffer, 4 * i, x[i]);
input[12]++;
return buffer;
}
但業績結果如預期不大:
與
而Firefox和Safari瀏覽器下的性能差異neglectible或不重要的Chrome下的性能切是巨大的...... 任何想法,爲什麼出現這種情況?
PS:如果圖像是小,在一個新的標籤:)
PP.S打開它們:這裏是鏈接:
評論是不適合擴展討論;這個對話已經[轉移到聊天](http://chat.stackoverflow.com/rooms/74430/discussion-on-question-by-k-biermann-strange-javascript-performance)。 – 2015-04-03 14:43:53
1)創建數組的成本很高:重新使用相同的緩衝區。 2)告訴我們你的U32TO8_LE,這可能是昂貴的。 3)在quarterRound中,緩存所有值,進行數學計算,然後存儲結果。這裏的高收益,我猜(8陣列間接而不是... 28!)。 4)你也可以考慮將8個函數與相關參數綁定,只將x更改爲最後一個參數而不是第一個參數。非常肯定的是,所有這些表演將會飛漲。 – GameAlchemist 2015-04-10 13:17:42