簡體   English   中英

JavaScript 數組性能在 13k-16k 元素時下降

[英]JavaScript array performance drop at 13k-16k elements

我正在對數組的創建和編輯性能進行一些性能測試,並注意到具有大約 13k-16k 元素的數組周圍有一些奇怪的特征。

下圖顯示了創建數組並從中讀取每個元素所需的時間(在這種情況下,將其中的數字相加)。 capacitypush與創建陣列的方式有關:

  • 容量const arr = new Array(length)然后arr[i] = data
  • const arr = []; 然后arr.push(data)

基於長度創建數組的時間 基於長度的數組操作時間

如您所見,在這兩種情況下,創建一個數組並從中讀取,與減少 1k 個元素的每個元素的性能相比,性能降低了大約 2-3 倍。
使用 push 方法創建數組時,與預先使用正確容量創建數組相比,此跳轉發生得更早一些。 我認為發生這種情況是因為,當推送到已經處於最大容量的陣列時,添加的額外容量超過了實際需要的額外容量(以避免很快再次添加新容量),並且達到了較慢性能路徑的閾值早些時候。

如果想看代碼或者自己測試: github

我的問題,為什么性能下降到 13k-16k 左右?

對我來說,在 v8 中似乎對較大的數組進行了不同的處理,從大約 13k-16k 元素開始以提高它們的性能,但是截止點(至少在我的代碼中)有點太早了,所以在優化之前性能下降了任何好處。

您可以看到性能改進在大約 500 個元素后下降,並在下降后再次上升。

可悲的是,我找不到任何有關此的信息。

另外,如果您碰巧知道為什么在使用容量創建和通過推送求和時會出現這些峰值,請隨時告訴我:)

編輯:

正如@ggorlen 所建議的那樣,我在另一台機器上運行相同的測試以排除緩存作為所見行為的原因(使用不同的、較弱的 CPU 和更少的 RAM)。 結果似乎非常相似。

在不同機器上根據長度創建數組的時間 在不同機器上根據長度對數組進行操作的時間

編輯:

我使用--allow-natives-syntax標志運行節點來調試使用%DebugPrint(array); ,希望看到不同數組長度之間的差異,但除了長度和內存地址之外,它們看起來都一樣。 這是一個例子:

 // For array created with capacity DebugPrint: 000002CB8E1ACE19: [JSArray] - map: 0x035206283321 <Map(HOLEY_SMI_ELEMENTS)> [FastProperties] - prototype: 0x036b86245b19 <JSArray[0]> - elements: 0x02cb8e1ace39 <FixedArray[1]> [HOLEY_SMI_ELEMENTS] - length: 1 - properties: 0x0114c5d01309 <FixedArray[0]> - All own properties (excluding elements): { 00000114C5D04D41: [String] in ReadOnlySpace: #length: 0x03f907ac1189 <AccessorInfo> (const accessor descriptor), location: descriptor } 0000035206283321: [Map] - type: JS_ARRAY_TYPE - instance size: 32 - inobject properties: 0 - elements kind: HOLEY_SMI_ELEMENTS - unused property fields: 0 - enum length: invalid - back pointer: 0x035206283369 <Map(PACKED_SMI_ELEMENTS)> - prototype_validity cell: 0x03f907ac15e9 <Cell value= 1> - instance descriptors #1: 0x009994a6aa31 <DescriptorArray[1]> - transitions #1: 0x009994a6a9d1 <TransitionArray[4]>Transition array #1: 0x0114c5d05949 <Symbol: (elements_transition_symbol)>: (transition to PACKED_DOUBLE_ELEMENTS) -> 0x0352062832d9 <Map(PACKED_DOUBLE_ELEMENTS)> - prototype: 0x036b86245b19 <JSArray[0]> - constructor: 0x031474c124e9 <JSFunction Array (sfi = 000003CECD93C3A9)> - dependent code: 0x0114c5d01239 <Other heap object (WEAK_FIXED_ARRAY_TYPE)> - construction counter: 0 // For array created with push DebugPrint: 000003B09882CE19: [JSArray] - map: 0x02ff94f83369 <Map(PACKED_SMI_ELEMENTS)> [FastProperties] - prototype: 0x0329b3805b19 <JSArray[0]> - elements: 0x03b09882ce39 <FixedArray[17]> [PACKED_SMI_ELEMENTS] - length: 1 - properties: 0x03167aa81309 <FixedArray[0]> - All own properties (excluding elements): { 000003167AA84D41: [String] in ReadOnlySpace: #length: 0x02094f941189 <AccessorInfo> (const accessor descriptor), location: descriptor } 000002FF94F83369: [Map] - type: JS_ARRAY_TYPE - instance size: 32 - inobject properties: 0 - elements kind: PACKED_SMI_ELEMENTS - unused property fields: 0 - enum length: invalid - back pointer: 0x03167aa81599 <undefined> - prototype_validity cell: 0x02094f9415e9 <Cell value= 1> - instance descriptors #1: 0x00d25122aa31 <DescriptorArray[1]> - transitions #1: 0x00d25122aa01 <TransitionArray[4]>Transition array #1: 0x03167aa85949 <Symbol: (elements_transition_symbol)>: (transition to HOLEY_SMI_ELEMENTS) -> 0x02ff94f83321 <Map(HOLEY_SMI_ELEMENTS)> - prototype: 0x0329b3805b19 <JSArray[0]> - constructor: 0x009ff8a524e9 <JSFunction Array (sfi = 0000025A84ABC3A9)> - dependent code: 0x03167aa81239 <Other heap object (WEAK_FIXED_ARRAY_TYPE)> - construction counter: 0

編輯

從大小為 13_994 的數組變為 13_995 時,求和的性能下降:

在此處輸入圖像描述

(這里是 V8 開發人員。)

這里有兩個單獨的效果:

(1) 在 16384 個元素處發生的情況是后備存儲被分配在“大對象空間”中,這是針對大對象優化的堆的特殊區域。 在啟用了指針壓縮的 Chrome 中,發生這種情況的元素數量恰好是禁用指針壓縮的 Node 中的兩倍。 其結果是分配本身不能再作為內聯指令序列直接在優化代碼中發生; 相反,它是對 C++ 函數的調用; 除了有一些調用開銷之外,它還是一個更通用的實現,因此速度有點慢(可能有一些優化潛力,不確定)。 所以這不是太早開始的優化。 這只是巨大的物體所付出的代價。 並且它只會在分配許多大型數組然后對它們沒有太多作用的情況下(微小的微基准測試?)突出顯示。

(2) 在 13995 個元素處發生的情況是對於特定函數sum ,即優化編譯器啟動“OSR”(堆棧上替換)函數時,即函數在運行時被優化代碼替換。 無論何時發生,這都是一定的一次性成本,並且很快就會收回成本。 因此,將其視為特定時間的特定命中是典型的微基准測試工件,與實際使用無關。 (例如,如果您在同一進程中多次運行測試,則不會再看到 13995 處的步驟。如果您以多種尺寸運行它,則可能不需要 OSR(因為該函數可以下次調用時切換到優化代碼)並且這種一次性成本根本不會發生。)

TL;DR:這里沒什么可看的,只是產生令人困惑的偽影的微基准。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM