2014-07-20 30 views
15

爲什麼TypedArray不比通常的數組快?我想爲CLZ使用precalc值(計算前導零功能)。我不希望他們像往常一樣解釋對象?Javascript TypedArray性能

http://jsperf.com/array-access-speed-2/2

製備代碼:

Benchmark.prototype.setup = function() { 
    var buffer = new ArrayBuffer(0x10000); 
    var Uint32 = new Uint32Array(buffer); 
    var arr = []; 
    for(var i = 0; i < 0x10000; ++i) { 
    Uint32[i] = (Math.random() * 0x100000000) | 0; 
    arr[i] = Uint32[i]; 
    } 
    var sum = 0; 
}; 

測試1:

sum = arr[(Math.random() * 0x10000) | 0]; 

試驗2:

sum = Uint32[(Math.random() * 0x10000) | 0]; 

enter image description here

PS可能是我的性能測試無效,隨時糾正我。

+0

您是否刪除了jsperf測試?我無法再訪問它們 – Bergi

+0

不,我沒有刪除它。這很奇怪。 –

+1

很奇怪,他們會像那樣消失!所有這些......根據[這個FAQ條目](http://jsperf.com/faq#test-availability)特別奇怪。我搜索過萬一URL改變了,或者什麼都沒有。我可以找到陣列表現的其他測試,但不是Sukhanov和我的。我提出了一個[問題](https://github.com/mathiasbynens/jsperf.com/issues/197)。當然,如果我們中的更多人[捐獻給jsPerf](http://jsperf.com/faq#donate)(我剛剛做了......第一次*鴨頭*),它可能不會受到傷害。 –

回答

27
var buffer = new ArrayBuffer(0x10000); 
var Uint32 = new Uint32Array(buffer); 

是不一樣的東西:

var Uint32 = new Uint32Array(0x10000); 

因爲新ArrayBuffer不(你總是得到一個數組緩衝區:看Uint32.buffer在這兩種情況下),但由於長度參數:與ArrayBuffer你有每個元素1個字節,Uint32Array每個元素4個字節。因此,在第一種情況下(和在你的代碼中),Uint32.length = 0x1000/4,並且你的循環超出4次3次。但遺憾的是,你永遠不會得到錯誤,只有糟糕的表演。

使用 '新ArrayBuffer',你必須聲明UINT32這樣的:

var buffer = new ArrayBuffer(0x10000 * 4); 
var Uint32 = new Uint32Array(buffer); 

jsperf with (0x10000)jsperf with (0x10000 * 4)

+3

人們接受基於內容大小而不是正確性的答案是一種恥辱。 – MorrisLiang

+1

@MorrisLiang,這就是我的想法,但後來我意識到這個答案在問題發佈半年後才發佈。 – user2033427

+0

@MorrisLiang:請注意,我的回答*總是使用'​​new Uint32Array(0x10000)'來比較蘋果和蘋果的長度,並且仍然顯示速度差異。但我並沒有像以往的radsoc那樣呼喚差異。 –

25

即使您使用Array,現代引擎也會在幕後使用真實數組,如果您認爲他們可以使用Array,如果您執行的操作使他們認爲無法使用真數組,則會回退到屬性映射「數組」。

還要注意,如radsoc points outvar buffer = new ArrayBuffer(0x10000)然後var Uint32 = new Uint32Array(buffer)產生UINT32數組,其尺寸是0x4000的(0x10000的/ 4),而不是爲0x10000,因爲你給ArrayBuffer值以字節爲單位,但當然也有每Uint32Array條目四個字節。以下全部使用new Uint32Array(0x10000)而不是(並且始終如此,甚至在此編輯之前)來比較蘋果和蘋果。

所以,讓我們從這裏開始,與new Uint32Array(0x10000)http://jsperf.com/array-access-speed-2/11(可悲的是,JSPerf已經失去了本次測試及其結果,而現在處於脫機狀態完全)

graph showing roughly equivalent performance

這表明,因爲你」以簡單,可預測的方式重新填充陣列,現代引擎繼續使用真正的陣列(以及其性能優勢),而不是轉移。我們看到兩者的表現基本相同。速度的差異可能與類型轉換有關,其中的值爲Uint32,並將其作爲sum作爲number(儘管如果該類型轉換未延遲,我感到很驚訝...)。

添加一些混亂,但:

var Uint32 = new Uint32Array(0x10000); 
var arr = []; 
for (var i = 0x10000 - 1; i >= 0; --i) { 
    Uint32[Math.random() * 0x10000 | 0] = (Math.random() * 0x100000000) | 0; 
    arr[Math.random() * 0x10000 | 0] = (Math.random() * 0x100000000) | 0; 
} 
var sum = 0; 

...使發動機具有依傍老式的屬性映射「陣列」,你看到被打的陣列明顯優於老式類型:http://jsperf.com/array-access-speed-2/3(可悲的是,JSPerf已經失去了這個測試及其結果)

bar graph showing marked performance improvement for typed arrays

聰明,這些JavaSc ript引擎工程師...

但是,您對Array數組的非數組屬性所做的具體事情很重要;考慮:

var Uint32 = new Uint32Array(0x10000); 
var arr = []; 
arr.foo = "bar";       // <== Non-element property 
for (var i = 0; i < 0x10000; ++i) { 
    Uint32[i] = (Math.random() * 0x100000000) | 0; 
    arr[i] = (Math.random() * 0x100000000) | 0; 
} 
var sum = 0; 

這仍然填充陣列可預見的,但我們非元素屬性(foo)添加到它。 http://jsperf.com/array-access-speed-2/4(可悲的是,JSPerf已經失去了這個測試及其結果)顯然,引擎是相當聰明,並保留非元素屬性到一邊,而繼續使用的元素屬性一個真正的數組:

bar graph showing performance improvement for standard arrays when <code>Array</code> array gets non-element property

我在一個小的損失來解釋爲什麼標準陣列應該得到更快有相比,我們的第一個測試上方。測量錯誤?迷宮在Math.random?但是我們仍然確信Array中的陣列特定數據仍然是一個真正的數組。

而如果我們做同樣的事情,但以相反的順序填寫:

var Uint32 = new Uint32Array(0x10000); 
var arr = []; 
arr.foo = "bar";       // <== Non-element property 
for (var i = 0x10000 - 1; i >= 0; --i) { // <== Reverse order 
    Uint32[i] = (Math.random() * 0x100000000) | 0; 
    arr[i] = (Math.random() * 0x100000000) | 0; 
} 
var sum = 0; 

...我們回到類型數組勝出  —除了在IE11:http://jsperf.com/array-access-speed-2/9(可悲的是,JSPerf有失去了這個測試及其結果)

graph showing typed arrays winning except on IE11

+2

有趣,謝謝。在實際使用情況下,情況會變得更加複雜,在這種情況下,各種對象/數組的連續分配/重新分配會導致內存碎片,並且如果未預先分配內存,處理時間會爆炸。在現實生活中,Typed Array會花費更少,因爲它們是預先分配的(Rq:js Arrays可能是預先分配的......) – GameAlchemist

+0

美麗的解釋,謝謝。 – qodeninja

+0

將預先調用的int32值寫入鍵入的數組很快。該陣列不包含洞(是的,洞很快,但...)。數組是預先分配的。所以創建和初始化類型化數組很快。但是當你嘗試從Uint32Array讀取時,該值應該被轉換爲double(見下面的答案)。您應該使用Int32Array(在x64上)或Int16Array(在x86上)(請參閱下面的答案)。 – vitaliydev

0

在你的情況下,性能不佳的原因是你嘗試使用Uint32Array讀取數組之外的數組,因爲數組長度錯誤。

但是,如果它不會是那麼的真實原因:

嘗試改用Uint32Array的Int32Array。我認爲在V8變量中不能有uint32類型,但可以有int32/double /指針。所以當你將uint32類型賦值給變量時,它將被轉換爲較慢的double。

如果使用32位版本的V8,那麼變量可以有int31/double /指針類型。所以int32也會被轉換成double。但是如果你使用普通的數組並且所有的值都是int31,那麼不需要轉換,所以通常的數組可以更快。

另外使用int16可能需要一些轉換來獲得int32(因爲符號和1的補碼)。 Uint16不需要轉換,因爲V8可以在左側添加零。

PS。你可能會感興趣的指針和int31(或x64上的int32)在V8中是一樣的。這也意味着int32在x64上需要8個字節。這也是x86上沒有int32類型的原因:因爲如果我們使用全部32位來存儲整數,我們就不會有任何空間來保存指針了。