:::: 菜单 ::::

AS3-非炼金高速内存访问的杂记

说实话,最近的项目,越做越无聊了,核心的东西基本完成,剩下的几乎都是体力活,没什么新鲜好玩的。昨天打完工人物语7,睡意正浓,突然看了两个提神的网页:http://www.buraks.com/azoth/http://philippe.elsass.me/2010/05/as3-fast-memory-access-without-alchemy/ ,这一下子精神就来了。

FlashPlayer中读写ByteArray速度应付某些变态需求的时候,总会力不从心,这两天徐灿被搞得非寻找个离经叛道的非官方手段不可,结果还就真找到了。我之前听闻haXe的高效率是因为作者找到了FlashPlayer10为炼金术提供的底层接口,但因为种种原因没有去使用haXe,看到这俩网页又让我看到了新的花样,原来是一个圈子里的货。它们三个都是找到了FlashPlayer10给炼金术开的“后门”在低层提高性能。

既然看到了新鲜玩意,不拿来玩弄玩弄就太不给力啦。haXe我依然不打算使用,我希望尽量少地依赖非官方的API。Apparat很好很强大,不过需要安装运行时,而且是一个功能很多的框架,我不喜欢用一个有大半功能都用不上的玩意。

Azoth是一个专门带来高速访问内存API的小工具,小巧轻便,功能唯一,我就喜欢这种。它让我们把一个ByteArray作为一个独立内存区使用,下载http://www.buraks.com/azoth/azoth104.zip然后用里面提供的fastmem类读写数据,写好代码编译成SWF后运行azoth.exe注入swf,就变成了优化版,没有注入的时候显然速度是比flash提供的API要慢的。

既然要玩弄,那就没事找事做实验。在徐灿的鼓动下,今天一时脑子发热,试了一下把位图渲染用这套高速内存访问来优化。基本思路是把所有用于渲染的BitmapData全部以二进制存放到一个ByteArray里,每个位图有一个起始内存地址和长度,手动管理这个ByteArray的所有内部地址,维护图像的层级内容,最终只有输出到视图的是一个BitmapData,以期高速读写带来性能提升。

试过之后发现效果不可观——尽管内存读写速度更快,但它让copypixel的实现负担放到了逐个像素处理上,一张仅300*300的图copypixel到另一张图,则需要计算90000 * 2个内存地址,更大的图更多的内存地址计算,这些计算量在高层实现是很耗时的。而且图与图之间的copypixel并不只是单纯的数值存取,还要根据alpha进行合并,颜色计算又要消耗时间。flash本身的bitmapData的copypixel,对300*300的图进行5次处理只要4ms,而flash里面执行180000次循环加法计算,在我机器上就要7ms。所以嘛,吞吐量不是问题,运算量极度加剧,高速内存存取不是用来干这个的,果然是脑子发热了。

延伸阅读


  1. 为什么是copyPixel而不是setPixels呢?既然用了byteArray就不必要使用并不显示的bitmapData作buffer了

  2. @Andy Shang, 我这里的copyPixel是自己管理内存实现的替代方法,并不是BitmapData的copyPixel。 而且,既然用了ByteArray,自然也不需要使用并不显示的bitmapData作buffer了,所以也并没有BitmapData的setPixels。

    1. public function copyPixels(sourceBitmapData : BmpData, sourceRect : Rectangle, destPoint : Point) : void
    2.         {
    3.             // 只根据地址做fastBytearray写入
    4.             var mem : ByteArray = _database.bmp_mem::mem;
    5.             var destX : int = destPoint.x;
    6.             var destY : int = destPoint.y;
    7.             var sourceX : int = sourceRect.x;
    8.             var sourceY : int = sourceRect.y;
    9.             var sourceH : int = sourceRect.height;
    10.             var sourceW : int = sourceRect.width;
    11.             fastmem.fastSelectMem(mem);
    12.  
    13.             var oldBmdAddr : uint;
    14.             var srcBmdAddr : uint;
    15.             // 逐个int处理
    16.             for ( var iy : int = 0 ; iy < sourceH;iy++)
    17.             {
    18.                 for ( var ix : int = 0 ;ix < sourceW ;ix++)
    19.                 {
    20.                     // 老位图的地址
    21.                     oldBmdAddr = getAddressByPoint(destX + ix, destY + iy, _width, _startAddr);
    22.                     // 要盖住我的地址
    23.                     srcBmdAddr = getAddressByPoint(sourceX + ix, sourceY + iy, sourceBitmapData.width, sourceBitmapData.startAddr);
    24.                     // 总是等于后者
    25.                     fastmem.fastSetI32(fastmem.fastGetI32(srcBmdAddr), oldBmdAddr);
    26.                 }
    27.             }
    28.             fastmem.fastDeselectMem();
    29.         }
  3. 明白:) 虽然并不太擅长render的优化,不过感官的认识,更大段的内存复制效率总是远胜于每个地址的迭代,一般都是索引到tile整个贴到dstmap去的。所以虽然用fastmem单次读写速度提升了,整体效率还是难得到提高。