android 上的 as3 工作线程通信问题

as3 worker thread communication issue on android

我在 android 的 AIR AS3 编码中遇到工作人员之间的通信速度问题。我的测试设备是 Galaxy S2 (android 4.0.4),我正在使用 AIR18.0 在 flashdevelop 中进行开发。

要事第一。 我尝试了通过共享对象复制的好旧 AMF 序列化。我在物理引擎(辅助线程)上获得平均 49 calculations/second,在主线程上稳定 60FPS。必须将其调高到 300 多个动态对象才能明显减速。

一切顺利,所以我开始了设备上的测试,那时候狗屎开始横盘整理。我得到的分数不到 1.5 steps/s.

开始更深入地挖掘,写一堆代码来检查到底是什么东西这么慢,我发现查看共享对象有点像看别人看油漆变干。

此时我开始深入研究。我发现已经有很多人在抱怨消息通道的速度(在共享对象上发现的不多,我猜 "developers" 现状)。所以我决定使用共享的字节数组和互斥锁来尽可能地降低。 (我跳过了条件,因为我特别不希望我的任何线程暂停)。

启动桌面调试器我得到 115-ish calculations/s 和超过 350 calculations/s 直接回调(调试器确实抛出异常,不是为那种连续处理而设计的我猜..任何人..)。 shared bytearray 和 mutexes 就像宣传的那样,比我前女友的性高潮还快。[=​​12=]

我在 S2 上进行了调试,结果我得到了 3.4 calculations/s 和 200 个动态对象。

所以.. 移动设备上的并发性对我来说已经差不多完成了。然后我想我做了一个没有任何交流的小测试。相同的场景,物理效果超过可接受的 40 calculations/s 和图形 运行 达到预期的 60FPS...

所以,我的直截了当的问题是:

FAPPING FIREFLY 是怎么回事?

这是我的 Com 代码:​​

package CCom 
{
    import Box2D.Dynamics.b2Body;
    import Box2D.Dynamics.b2World;
    import flash.concurrent.Condition;
    import flash.concurrent.Mutex;
    import flash.utils.ByteArray;
    import Grx.DickbutImage;
    import Phx.PhxMain;

    /**
     * shared and executed across all threads.
     * provides access to mutex and binary data.
     * 
     * @author szeredai akos
     */
    public class CComCore 
    {
        //===============================================================================================//
        public static var positionData:ByteArray = new ByteArray();
        public static var positionMutex:Mutex = new Mutex();

        public static var creationData:ByteArray = new ByteArray();
        public static var creationMutex:Mutex = new Mutex();

        public static var debugData:ByteArray = new ByteArray();
        public static var debugMutex:Mutex = new Mutex();

        //===============================================================================================//
        public function CComCore() 
        {

            positionData.shareable = true;
            creationData.shareable = true;
            debugData.shareable = true;
        }
        //===============================================================================================//
        public static function encodePositions(w:b2World):void
        {
            var ud:Object;

            positionMutex.lock();
            positionData.position = 0;
            for (var b:b2Body = w.GetBodyList(); b; b = b.GetNext())
            {
                ud = b.GetUserData();
                if (ud && ud.serial)
                {
                    positionMutex.lock();
                    positionData.writeInt(ud.serial);               // serial
                    positionData.writeBoolean(b.IsAwake);           // active state
                    positionData.writeInt(b.GetType())              // 0-static 1-kinematic 2-dynamic
                    positionData.writeDouble(b.GetPosition().x / PhxMain.SCALE);    // x
                    positionData.writeDouble(b.GetPosition().y / PhxMain.SCALE);    // y
                    positionData.writeDouble(b.GetAngle());         // r in radians
                }
            }
            positionData.length = positionData.position;
            positionMutex.unlock();

        }
        //===============================================================================================//
        public static function decodeToAry(ar:Vector.<DickbutImage>):void
        {
            var index:int;
            var rot:Number = 0;

            positionData.position = 0;
            while (positionData.bytesAvailable > 0)
            {
                //positionMutex.lock();
                index = positionData.readInt();
                positionData.readBoolean();
                positionData.readInt();
                ar[index].x -= (ar[index].x - positionData.readDouble()) / 10;
                ar[index].y -= (ar[index].y - positionData.readDouble()) / 10;
                ar[index].rotation = positionData.readDouble();
                //positionMutex.unlock();
            }

        }
        //===============================================================================================//
    }

}

(忽略位置y-=(y-x)/c上的低通滤波器)

所以。 请注意,仅在解析物理时使用互斥体确实可以将性能提高约 20%,同时对主线程的帧率影响最小。这使我相信问题不在于每次说的数据的写入和读取,而在于该数据可用于第二个线程的速度。我的意思是,..那些是 bytearray ops,它很快是很自然的。我确实通过简单地将远程线程转储到主线程来检查速度,速度仍然很好。地狱,..即使在 S2 上也可以接受,而无需放弃额外的计算。

ps: 我也试过正式版。

如果没有人有可行的解决方案(除了 .2-.4s 缓冲区和明显的单线程),我确实想听听糟糕的解决方法或至少是问题的具体来源。

提前谢谢

我想我找到了问题所在。 一如既往,事情比最初想象的要复杂。

定时器事件,以及设置的间隔和超时都限制在60fps。只要应用程序在该特定点空闲或在它可以自由执行并且延迟已经过去之后立即执行,计时器就会按时执行。但显然,延迟不能短于 15 左右(我猜它在台式机上更短)。应该没问题吧?

但是

如果那段代码操纵共享对象,计时器突然决定自己拉屎并查看这 15 毫秒,不管它是否有空闲时间。

无论如何,问题是共享对象、工作程序、计时器事件和 adobe 强加的 60FPS 限制之间存在错误交互。

解决方法非常简单。让定时器有 5000 毫秒这样的大规模延迟,并在定时器事件的回调中执行 5000 次循环。显然,在 5000 循环完成之前,下一个计时器事件不会触发,但最重要的是,它也不会增加巨大的延迟。

另一个奇怪的事情是在 5000 循环期间互斥量的贪婪所有权,所以必须使用 flash.concurrent.Condition。

好在性能提升是存在的,而且令人印象深刻。

缺点是整个物理事物现在密切锁定到主线程的帧速率(或主游戏循环包含的任何装置),但是嘿。我想 60Fps 已经足够了。

Zi MuleTrex-Condition 感兴趣的人:

package CCom 
{
    import Box2D.Dynamics.b2Body;
    import Box2D.Dynamics.b2World;
    import flash.concurrent.Condition;
    import flash.concurrent.Mutex;
    import flash.utils.ByteArray;
    import Grx.DickbutImage;
    import Phx.PhxMain;

    /**
     * shared and executed across all threads.
     * provides access to mutex and binary data.
     * 
     * @author szeredai akos
     */
    public class CComCore 
    {
        //===============================================================================================//
        public static var positionData:ByteArray = new ByteArray();
        public static var positionMutex:Mutex = new Mutex();
        public static var positionCondition:Condition = new Condition(positionMutex);

        public static var creationData:ByteArray = new ByteArray();
        public static var creationMutex:Mutex = new Mutex();

        public static var debugData:ByteArray = new ByteArray();
        public static var debugMutex:Mutex = new Mutex();

        //===============================================================================================//
        public function CComCore() 
        {

            positionData.shareable = true;
            creationData.shareable = true;
            debugData.shareable = true;
        }
        //===============================================================================================//
        public static function encodePositions(w:b2World):void
        {
            var ud:Object;

            positionData.position = 0;
            positionMutex.lock();
            for (var b:b2Body = w.GetBodyList(); b; b = b.GetNext())
            {
                ud = b.GetUserData();
                if (ud && ud.serial)
                {

                    positionData.writeBoolean(b.IsAwake);           // active state
                    positionData.writeInt(ud.serial);               // serial
                    positionData.writeInt(b.GetType())              // 0-static 1-kinematic 2-dynamic
                    positionData.writeDouble(b.GetPosition().x / PhxMain.SCALE);    // x
                    positionData.writeDouble(b.GetPosition().y / PhxMain.SCALE);    // y
                    positionData.writeDouble(b.GetAngle());         // r in radians

                }
            }
            positionData.writeBoolean(false);
            positionCondition.wait();

        }
        //===============================================================================================//
        public static function decodeToAry(ar:Vector.<DickbutImage>):void
        {
            var index:int;
            var rot:Number = 0;


            positionMutex.lock();
            positionData.position = 0;
            while (positionData.bytesAvailable > 0 && positionData.readBoolean())
            {
                //positionMutex.lock();
                index = positionData.readInt();
                positionData.readInt();
                ar[index].x = positionData.readDouble();
                ar[index].y = positionData.readDouble();
                ar[index].rotation = positionData.readDouble();
                //positionMutex.unlock();


            }
            positionCondition.notify();
            positionMutex.unlock();
        }
        //===============================================================================================//
    }

}

随着更多通道和 byteArray 开始弹出,同步将变得更加复杂。