簡體   English   中英

為數組賦值的性能

[英]Performance of assigning values to array

代碼優化在這里說,分析是優化javascript的第一步,建議的引擎是Chrome和Firefox的分析器。 這些問題是他們以某種奇怪的方式告訴每個函數執行的時間,但我對它們沒有很好的理解。 最有用的方法是分析器會告訴每行執行的次數,以及每行可能花費的時間。 這樣就可以嚴格地看到瓶頸。 但在實施/找到此類工具之前,我們有兩種選擇:

1)制作自己的計算器,計算某個代碼塊或行執行的時間和次數2)學會理解哪些是慢速方法,哪些不是

對於選項2, jsperf.com非常有幫助。 我試圖學習優化數組並在JSPERF.COM中進行速度測試。 下圖顯示了5個主要瀏覽器的結果,發現了一些我之前不知道的瓶頸。

速度測試

主要調查結果如下:

1)盡管使用哪種方法進行分配,但為數組分配值要比分配給正常變量要慢得多。

2)在性能關鍵循環之前預初始化和/或預填充陣列可以顯着提高速度

3)與將數值推入數組相比,數學三角函數不是那么慢(!)

以下是每項測試的解釋:


1. non_array(100%):

變量以這種方式給出了預定義的值:

var non_array_0=0;
var non_array_1=0;
var non_array_2=0;
...

在定時區域,他們被稱為這樣:

non_array_0=0;
non_array_1=1;
non_array_2=2;
non_array_3=3;
non_array_4=4;
non_array_5=5;
non_array_6=6;
non_array_7=7;
non_array_8=8;
non_array_9=9;

上面是一個類似數組的變量,但似乎無法以其他方式迭代或引用這些變量作為對陣列的反對。 還是有嗎?

此測試中的任何內容都不比為變量賦值更快。


2. non_array_non_pre(83.78%)

與測試1完全相同,但變量未預先初始化或預先填充。 速度是測試速度的83.78%。在每個測試的瀏覽器中,預填充變量的速度比未預先填充的速度快。 因此,在任何速度關鍵循環之外初始化(並可能預填充)變量。

測試代碼在這里:

var non_array_non_pre_0=0;
var non_array_non_pre_1=0;
var non_array_non_pre_2=0;
var non_array_non_pre_3=0;
var non_array_non_pre_4=0;
var non_array_non_pre_5=0;
var non_array_non_pre_6=0;
var non_array_non_pre_7=0;
var non_array_non_pre_8=0;
var non_array_non_pre_9=0;

3. pre_filled_array(19.96%):

陣列是邪惡的! 當我們丟棄正常變量(test1和test2)並將數組放入圖片時,速度會顯着降低。 雖然我們進行了所有優化(預初始化和預填充數組),然后直接分配值而不進行循環或推送,但速度降低到19.96%。 這非常難過,我真的不明白為什么會這樣。 這是我在這次測試中受到的主要沖擊之一。 數組是如此重要,我沒有找到一種方法來制作沒有數組的很多東西。

測試數據在這里:

pre_filled_array[0]=0;
pre_filled_array[1]=1;
pre_filled_array[2]=2;
pre_filled_array[3]=3;
pre_filled_array[4]=4;
pre_filled_array[5]=5;
pre_filled_array[6]=6;
pre_filled_array[7]=7;
pre_filled_array[8]=8;
pre_filled_array[9]=9;

4. non_pre_filled_array(8.34%):

這與3的測試相同,但是數組成員不是預先初始化的,也不是預先var non_pre_filled_array=[]; ,只有優化是事先初始化數組: var non_pre_filled_array=[];

與預先確定的測試3相比,速度降低了58,23%。 因此預先初始化和/或預填充陣列使速度加倍。

測試代碼在這里:

non_pre_filled_array[0]=0;
non_pre_filled_array[1]=1;
non_pre_filled_array[2]=2;
non_pre_filled_array[3]=3;
non_pre_filled_array[4]=4;
non_pre_filled_array[5]=5;
non_pre_filled_array[6]=6;
non_pre_filled_array[7]=7;
non_pre_filled_array[8]=8;
non_pre_filled_array[9]=9;

5. pre_filled_array [i](7.10%):

然后到循環。 這個測試中最快的循環方法。 陣列被預先初始化並預先填充。

與內聯版本(測試3)相比,速度下降為64.44%。 這是非常顯着的差異,我想說,如果不需要,不要循環。 如果數組大小很小(不知道它有多小,它必須單獨測試),使用內聯賦值而不是循環是更明智的。

而且因為速度下降是如此巨大而且我們確實需要循環,所以找到更好的循環方法 (例如while(i--) )是明智的。

測試代碼在這里:

for(var i=0;i<10;i++)
{
  pre_filled_array[i]=i;
}

6. non_pre_filled_array [i](5.26%):

如果我們不預先初始化和預填充陣列,速度會降低25,96%。 同樣,在速度關鍵循環之前預先初始化和/或預填充是明智的。

代碼在這里:

for(var i=0;i<10;i++) 
{
  non_pre_filled_array[i]=i;
}

7.數學計算(1.17%):

每個測試都必須是一些參考點。 數學函數被認為是緩慢的。 測試包括十次“重”數學計算,但現在又出現了另一件令我印象深刻的測試結果。 看看8和9的速度,我們將十個整數推入循環中的數組。 計算這10個Math函數比將10個整數推入循環數組快30%以上。 因此,可能更容易將一些數組推送轉換為預初始化的非數組並保留這些三角函數。 當然,如果每幀有數百或數千個計算,那么使用例如是明智的。 sqrt而不是sin / cos / tan並且使用出租車距離進行距離比較和鑽石角度(t弧度)進行角度比較 ,但仍然是主要的瓶頸可能在其他地方:循環比內聯慢,推動比使用直接賦值慢預處理和/或預填充,代碼邏輯,繪圖算法和DOM訪問可能很慢。 所有這些都無法在Javascript中進行優化(我們必須在屏幕上看到一些東西!)但是我們能做到的所有簡單而重要的事情都是明智之舉。 SO中的某個人說,代碼是針對人類的,可讀代碼比快速代碼更重要,因為維護成本是最大的成本。 這是經濟的觀點,但我發現代碼優化可以獲得兩者:優雅和可讀性以及性能。 如果達到5%的性能提升並且代碼更直接,它會給人一種良好的感覺!

代碼在這里:

non_array_0=Math.sqrt(10435.4557);
non_array_1=Math.atan2(12345,24869);
non_array_2=Math.sin(35.345262356547);
non_array_3=Math.cos(232.43575432);
non_array_4=Math.tan(325);
non_array_5=Math.asin(3459.35498534536);
non_array_6=Math.acos(3452.35);
non_array_7=Math.atan(34.346);
non_array_8=Math.pow(234,222);
non_array_9=9374.34524/342734.255;

8. pre_filled_array.push(i)(0.8%):

推是邪惡的! 推動組合循環是惡毒的邪惡! 這是由於某種原因將值分配給數組的非常慢的方法。 測試5(循環中的直接賦值)比這種方法快近9倍,並且兩種方法完全相同:將整數0-9分配給預初始化和預填充數組。 我還沒有測試過這種推送循環的惡意是由於推送或循環還是兩者的組合或循環計數。 在JSPERF.COM中有其他示例會產生相互矛盾的結果。 用實際數據進行測試並做出決策是明智之舉。 此測試可能與使用的其他數據不兼容。

以下是代碼:

for(var i=0;i<10;i++)
{
  pre_filled_array.push(i);
}

9. non_pre_filled_array.push(i)(0.74%):

此測試中的最后和最慢的方法與測試8相同,但不預先填充陣列。 略慢於9,但差異不顯着(7.23%)。 但是讓我們舉一個例子,將這種最慢的方法與最快的方法進行比較。 該方法的速度是方法1的速度的0.74%,這意味着方法1比這快135倍。 所以仔細想想,如果在特定的用例中完全需要數組。 如果它只有一次或幾次推動,則總速度差異不明顯,但另一方面如果只有很少的推動,則轉換為非數組變量非常簡單和優雅。

這是代碼:

for(var i=0;i<10;i++)
{
  non_pre_filled_array.push(i);
}

最后是強制性的SO問題:

因為根據這個測試的速度差異在非數組變量賦值和數組賦值之間似乎是如此巨大,有沒有什么方法可以獲得非數組變量賦值的速度和數組的動態?

我不能在循環中使用var variable_$i = 1 ,以便將$ i轉換為某個整數。 我必須使用var variable[i] = 1 ,這比測試證明的var variable1 = 1要慢得多。 只有存在大型陣列且在很多情況下它們才有用,這可能是至關重要的。


編輯:我做了一個新的測試,以確認數組訪問的緩慢,並試圖找到更快的方法:

http://jsperf.com/read-write-array-vs-variable

數組讀取和/或數組寫入比使用普通變量要慢得多。 如果對數組成員執行某些操作,則將數組成員值存儲到臨時變量更明智,將這些操作設置為temp變量,最后將值存儲到數組成員中。 雖然代碼變得越來越大,但是使這些操作內聯比使用循環要快得多。

結論:數組與正常變量類似於磁盤與內存。 通常,內存訪問比磁盤訪問更快,而正常變量訪問比數組訪問更快。 並且可能連接操作也比使用中間變量更快,但這使代碼有點不可讀。


為數組賦值比分配給常規變量要慢得多。 陣列是邪惡的! 這非常難過,我真的不明白為什么會這樣。 數組非常重要!

這是因為正常變量是靜態范圍的,並且可以(並且)可以輕松優化。 編譯器/解釋器將學習它們的類型,甚至可以避免重復分配相同的值。

這些優化也將針對數組進行,但它們並不那么容易,需要更長時間才能生效。 解析屬性引用時會有額外的開銷,並且由於JavaScript數組是自動增長列表,因此也需要檢查長度。

預先填充數組將有助於避免容量更改的重新分配,但對於您的小數組( length = 10),它應該沒有太大的區別。

有沒有什么方法可以獲得非數組變量分配的速度和數組的動態?

不,動力學確實有成本,但它們是值得的 - 就像循環一樣。

你幾乎不需要這樣的微優化, 不要嘗試它 在處理ImageData ,我唯一能想到的是固定大小的循環(n <= 4),內聯是適用的。

推是邪惡的!

不,只有你的測試有缺陷。 jsperf片段在定時循環中執行,沒有撕下和向下,只有你重置大小。 你的重復push已經產生了長度為數十萬的數組,並且需要相應的內存(重新)分配。 請訪問http://jsperf.com/pre-filled-array/11上的控制台。

實際上push與財產分配一樣快。 良好的測量很少,但那些正確完成的測量顯示不同瀏覽器引擎版本的不同結果 - 快速變化和意外。 請參見如何將某些內容附加到數組中? 為什么array.push有時比array [n] = value更快? 有沒有理由JavaScript開發人員不使用Array.push()? - 結論是你應該使用最適合你的用例的東西,而不是你認為更快的東西。

暫無
暫無

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

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