【译】AS3利用CPU缓存

简介:

利用CPU缓存

 

计算机有随机存取存储器RAM(译注:即我们常说的内存),但有更快形式的存储器。如果你希望你的应用程序的快速运行,你需要知道这些其他的存储器。今天的文章中讨论了它们,并给出了两个AS3例子,即使有这样的高级语言,你仍然可以利用它们。

RAM的确很快,但只是与硬盘,固态硬盘,光盘,互联网等等与比较时。RAMCPU内置的高速缓存相比,它并不快。你可能已经听说过他们,CPU高速缓存的级别分别称为:L1L2L3

CPU高速缓存用来存储小块的RAM内容。当RAM被请求时,可以使用更快的高速缓存,而不是...但只有当所请求的RAM是在高速缓存中的。这就是所谓的高速缓存“命中”。如果高速缓存“miss”,CPU需要从较慢的RAM来获取。

CPU的高速缓存策略各不相同,但RAM连续块通常存储在缓存中(译注:因为程序运行时对内存的访问呈现局部性(Locality)特征。这种局部性既包括空间局部性(Spatial Locality),也包括时间局部性(Temporal Locality)。有效利用这种局部性,缓存可以达到极高的命中率。不清楚的可以回去补下课)。这意味着,如果你的应用程序的工作在连续的内存块(如一个Vector)上并且大小不超过高速缓存大小限制,那么CPU高速缓存将被命中,而不是从RAM中读取,将收获一个大的性能取胜。

用一个有点做作的例子来证明这一点,访问一个大Vector中所有的元素模拟RAM。分别顺序访问,随机访问它们。为了消除Math.random调用的影响,通过2Vector来存储访问元素的索引值。在随机访问的情况下,Vector中存的索引值是随机的,这样元素将被随机读取。下面是10个元素的向量的例子,两个索引值Vector可能如下所示:

/ /顺序访问(索引Vector中存储的index值)

0 1 2 3 4 5 6 7 8 9

/ /随机访问(索引Vector中存储的index值)

2 3 9 1 4 0 6 8 5 7

测试运行的环境:

Release version of Flash Player 12.0.0.41

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

 

clip_image002[4]

由此我们可以看到使用了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实例很大的时候问题开始真正出现。如果BitmapData2048×2,前四个内存访问将是0204712048

下面的例子比较了2048×2048BitmapData这两种循环策略,测试环境与上面的相同:

BITMAPDATA外循环

耗时

89

348

 

clip_image004[4]

这里的性能优势不像上面访问Vector那么大,但遍历相同的像素时它仍然是高达4倍的优势。如果BitmapData是更大的,优势只会更大。这在CPU的缓存比我的测试环境中少的情况更明显。

下面是测试应用程序的源代码:


 

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)。

相关文章
|
Docker 容器
CPU内存不足分析Gitlab的内存消耗
CPU内存不足分析Gitlab的内存消耗
CPU内存不足分析Gitlab的内存消耗
|
3天前
|
存储
CPU的内存分页
CPU的内存分页是一种内存管理机制,旨在优化内存的使用效率和程序的运行效率。在现代计算机系统中,整个虚拟和物理内存空间被切割成固定大小的块,称为页(Page)和帧(Frame)。页用于虚拟地址空间,而帧用于物理内存空间。这些页和帧的大小通常是固定的,比如常见的4KB。 CPU通过内存管理单元(MMU)来实现虚拟地址到物理地址的转换。这个转换过程是通过页表来完成的,页表存储在内存中,并保存了页号与页帧号的映射关系。当CPU需要访问某个虚拟地址时,它会查阅页表,找到对应的物理地址,然后完成内存访问。 内存分页的主要好处有以下几点: 1. **减小换入换出的粒度**:内存分页允许操作系统以更小
6 0
|
2月前
|
存储 缓存 Linux
CPU高速缓存架构
CPU高速缓存架构
|
4月前
|
缓存 Java 知识图谱
1.什么是CPU多级缓存模型?
1.什么是CPU多级缓存模型?
55 0
1.什么是CPU多级缓存模型?
|
7月前
|
Java
CPU及并发
CPU及并发
105 0
|
10月前
|
缓存
CPU、处理器、内存、外存、寄存器、缓存的区别
CPU、处理器、内存、外存、寄存器、缓存的区别
1263 0
|
存储 缓存 算法
CPU缓存刷新的误解
即使是资深的技术人员,我经常听到他们谈论某些操作是如何导致一个CPU缓存的刷新。看来这是关于CPU缓存如何工作和缓存子系统如何与执行核心交互的一个常见误区。本文将致力于解释CPU缓存的功能以及执行程序指令的CPU核心如何与缓存交互。我将以最新的Intel x86 CPU为例进行说明,其他CPU也使用相似技术以达到相同目的。
176 0
CPU缓存刷新的误解
|
缓存 编译器
CPU缓存和内存屏障
CPU性能优化手段-缓存 为了提高程序运行的性能,现代CPU在很多方面对程序进行了优化。例如:CPU高速缓存。尽可能地避免处理器访问主内存的时间开销,处理器大多会利用缓存(cache)以提高性能。 多级缓存 L1 Cache(一级缓存)是CPU第一层高速缓存,分为数据缓存和指令缓存。
908 0
|
缓存 存储 NoSQL
进程内缓存,究竟怎么玩?
除了常见的redis/memcache等进程外缓存服务,缓存还有一种常见的玩法,进程内缓存。
598 0
|
存储 缓存 测试技术