RAM的確很快,但只是與硬盤,固態硬盤,光盤,互聯網等等與比較時。RAM與CPU內置的高速緩存相比,它並不快。你可能已經據說過他們,CPU高速緩存的級別分別稱爲:L1,L2和L3。緩存
CPU高速緩存用來存儲小塊的RAM內容。當RAM被請求時,能夠使用更快的高速緩存,而不是...但只有當所請求的RAM是在高速緩存中的。這就是所謂的高速緩存「命中」。若是高速緩存「miss」,CPU須要從較慢的RAM來獲取。app
CPU的高速緩存策略各不相同,但RAM連續塊一般存儲在緩存中(譯註:由於程序運行時對內存的訪問呈現局部性(Locality)特徵。這種局部性既包括空間局部性(Spatial Locality),也包括時間局部性(Temporal Locality)。有效利用這種局部性,緩存能夠達到極高的命中率。不清楚的能夠回去補下課)。這意味着,若是你的應用程序的工做在連續的內存塊(如一個Vector)上而且大小不超太高速緩存大小限制,那麼CPU高速緩存將被命中,而不是從RAM中讀取,將收穫一個大的性能取勝。dom
用一個有點作做的例子來證實這一點,訪問一個大Vector中全部的元素模擬RAM。分別順序訪問,隨機訪問它們。爲了消除Math.random調用的影響,經過2個Vector來存儲訪問元素的索引值。在隨機訪問的狀況下,Vector中存的索引值是隨機的,這樣元素將被隨機讀取。下面是10個元素的向量的例子,兩個索引值Vector可能以下所示:oop
/ /順序訪問(索引Vector中存儲的index值)性能 0 1 2 3 4 5 6 7 8 9測試 / /隨機訪問(索引Vector中存儲的index值)ui 2 3 9 1 4 0 6 8 5 7spa |
測試運行的環境:debug
Release version of Flash Player 12.0.0.41code
2.3 Ghz Intel Core i7-3615QM (256 KB L2 per core, 6 MB L3 cache)
Mac OS X 10.9.1
Google Chrome 32.0.1700.77
ASC 2.0.0 build 354071 (-debug=false -verbose-stacktraces=false -inline -optimize=true)
測試獲得的結以下:
訪問方式 |
耗時 |
順序 |
27 |
隨機 |
169 |
由此咱們能夠看到使用了CPU緩存的順序訪問,比沒有使用CPU緩存的隨機訪問性能高6+倍。訪問內存的順序真的很重要!
誠然上面的例子有點作做,但下面的例子是處理實際問題:遍歷BitmapData的像素。須要一個循環來遍歷行(Y),一個循環來遍歷列(X)。哪一個做爲外循環,哪一個做爲內循環?
實際上,BitmapData是「逐行」存儲的。下面是3x2 BitmapData:
(0,0) (1,0) (2,0) (0,1) (1,1) (1,2)
若是外循環是遍歷列(X),訪問這些內存的地址以下:
0 4 8 1 5 9 2 6 10 3 7 11
可是若是外循環是遍歷行(Y),訪問順序以下:
0 1 2 3 4 5 6 7 8 9 10 11
正如咱們上面所瞭解到,順序訪問能夠利用CPU緩存。彷佛只是跳序了一點點,可是當BitmapData實例很大的時候問題開始真正出現。若是BitmapData爲2048×2,前四個內存訪問將是0,2047,1,2048。
下面的例子比較了2048×2048的BitmapData這兩種循環策略,測試環境與上面的相同:
BITMAPDATA外循環 |
耗時 |
行 |
89 |
列 |
348 |
這裏的性能優點不像上面訪問Vector那麼大,但遍歷相同的像素時它仍然是高達4倍的優點。若是BitmapData是更大的,優點只會更大。這在CPU的緩存比個人測試環境中少的狀況更明顯。
下面是測試應用程序的源代碼:
Code: |
package { import flash.display.BitmapData; import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.utils.getTimer; public class CacheTest extends Sprite { private var logger:TextField = new TextField(); private function row(...cols): void { logger.appendText(cols.join(",")+"\n"); } public function CacheTest() { stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP_LEFT; logger.autoSize = TextFieldAutoSize.LEFT; addChild(logger); testVector(); row(); testBitmapData(); } private function testVector(): void { const SIZE:int = 10000000; var beforeTime:int; var afterTime:int; var i:int; var sum:int; // Build vectors of indices var inOrder:Vector.<int> = new Vector.<int>(SIZE); for (i = 0; i < SIZE; ++i) { inOrder[i] = i; } var randomOrder:Vector.<int> = inOrder.slice(); shuffle(randomOrder); row("Vector Access", "Time"); sum = 0; beforeTime = getTimer(); for (i = 0; i < SIZE; ++i) { sum += inOrder[inOrder[i]]; } afterTime = getTimer(); row("Sequential", (afterTime-beforeTime)); sum = 0; beforeTime = getTimer(); for (i = 0; i < SIZE; ++i) { sum += randomOrder[randomOrder[i]]; } afterTime = getTimer(); row("Random", (afterTime-beforeTime)); } private function testBitmapData(): void { var SIZE:int = 2048; var bmd:BitmapData = new BitmapData(SIZE, SIZE, true, 0xff123456); var curRow:int; var curCol:int; var sum:int; var beforeTime:int; var afterTime:int; row("BitmapData Outer Loop", "Time"); sum = 0; beforeTime = getTimer(); for (curRow = 0; curRow < SIZE; ++curRow) { for (curCol = 0; curCol < SIZE; ++curCol) { sum += bmd.getPixel32(curCol, curRow); } } afterTime = getTimer(); row("Row", (afterTime-beforeTime)); sum = 0; beforeTime = getTimer(); for (curCol = 0; curCol < SIZE; ++curCol) { for (curRow = 0; curRow < SIZE; ++curRow) { sum += bmd.getPixel32(curCol, curRow); } } afterTime = getTimer(); row("Col", (afterTime-beforeTime)); } private function shuffle(vec:Vector.<int>): void { var len:uint = vec.length; while (len) { var index:uint = Math.floor(Math.random() * len); len--; var temp:int = vec[len]; vec[len] = vec[index]; vec[index] = temp; } } } }
|
運行測試程序(http://files.jacksondunstan.com/articles/2491/CacheTest.swf)。