不变性和Flex是一个糟糕的组合吗?

问题描述 投票:3回答:3

在我的小划痕构建的flex游戏框架中,我定义了一个名为ThreeDPoint的类,它带有一个x,y和z坐标集来跟踪我的游戏内对象,称为Actors。我还使用该类来创建堆叠的运动矢量,并将每个帧添加到一起以为每个Actor创建累积运动矢量。

我使ThreeDPoint类成为不可变的,以支持给定位置不能改变的想法,你只能给一个Actor一个新的位置,并且也阻止潜在的客户程序员(我!)改变堆栈中的运动向量,而不是分配一个新的运动矢量来创建你想要的那种运动。

不幸的是,这个系统的性能急剧下降。使用Flex Builder探查器,我注意到我正在泄漏一些ThreeDPoint对象(我有26个Actors,我应该有大约30个,但运行时只需60秒就能带来超过1000个这样的对象),但因为对象如此轻量级,实际内存占用量相当稳定。

另一方面,分析器显示在运行60秒后累积创建的250,000多个ThreeDPoint对象。现在,正如我故意创造和丢弃这些物体一样,这对我来说似乎并不奇怪。但是,在看到这样的配置文件时,唯一想到的是大量的new()和GC调用(不是我没有明确地调用GC)是杀死性能的原因,特别是考虑到这样的事实:当我开始时,ThreeDPoint是可变的,一切都很顺利。这看起来合情合理吗?

package net.emptykingdom.utils
{
    public class ThreeDPoint
    {
        public function ThreeDPoint(x:Number = 0, y:Number = 0, z:Number = 0)
        {
            this._x = x;
            this._y = y;
            this._z = z;
        }

        public function get X():Number { return _x; }
        public function get Y():Number { return _y; }
        public function get Z():Number { return _z; }

        private var _x:Number = 0;
        private var _y:Number = 0;
        private var _z:Number = 0;
    }
}

编辑:我发现并删除了内存泄漏。它已经产生了一个小而明显的性能增益,虽然没有那么大,以至于能够实例化大量的Actors。根据分析器,我的代码仍然由调用ThreeDPoint构造函数控制。回到可变的ThreeDPoint让我回到了我曾经享受过的相当多的表现。所以我猜Flex对象实例化比我玩过的其他环境更昂贵。太糟糕了。

flex actionscript-3 immutability
3个回答
4
投票

您的描述非常有趣,而您的怀疑 - 通过使ThreeDPoint类不可变而进行预优化已经破坏了您的性能 - 听起来是正确的。你基本上是通过交换整个对象来替换改变对象的内容(可变),并假设gc和运行时会更好。正如你所说,实例化和gc调用正在使现在的工作变得简单。所以你只有几个可能性:

  1. 你在某个地方持有一些引用,或者创造出比你想要的更多的这些对象,这就是在扼杀你的表现
  2. 通过减少gc射击的次数,您可以帮助您的表现。我怀疑这一点,但至少你可以try it out
  3. 可变设计更好。不是理论上的,但Flash引擎就是这样看的。

如果这个问题对你来说真的很有意思,那就把它减少到它的核心元素(可变与不可变,大量的对象创建与变异),并展示你在某些测试中的预感。然后将结果发回这里,这样我们都可以变得更聪明。

我的猜测:这是一个明显的例子,试图为Flash引擎提供无用的帮助。

编辑:更好地阅读您的第一段我意识到您在程序中出于设计原因而这样做。如果是这种情况,不幸的是,实时编程是OO设计满足运行时引擎的严酷现实的地方。


2
投票

您每个Actor每秒创建160个ThreeDPoints。在30 FPS时,每帧每个演员约为5。这样每个Actor每个帧可以进行15次调用,仅用于读出ThreeDPoints的坐标。我相信,这无法无限扩展。

Actor::moveTo(x:Number, y:Number, z:Number):voidActor::moveBy(x:Number, y:Number, z:Number):void有什么不对?

此外,对于游戏,我认为这是一个更好的演员模型(只是草图):

package {
    public class Actor {
        private var _x:Number;
        private var _y:Number;
        private var _z:Number;
        public var xs:Number;
        public var ys:Number;
        public var zs:Number;

        public function Actor() {}
        public function get x():Number { return _x; }
        public function get y():Number { return _y; }
        public function get z():Number { return _z; }
        public function step():void {
            this.x += this.xs;
            this.y += this.ys;
            this.z += this.zs;
        }                  
    }   
}

以下界面摘录了对Actor的所有影响(力量如摩擦,重力,推力等)。

package {
    interface IActorAffector {
        function applyTo(actor:Actor):void;
    }   
}

只是以恒定速度移动的Actor只需要调用步骤。而已。您只需要在IActorAffector上创建可以对目标起作用的每个效果(吸引力来源等)

不变性不是那么错,但在你的情况下似乎太贵了。此外,如果您正在管理ThreeDPoints,您可能希望使用对象池来保持实例化和GC低。

如果事情对性能至关重要,你可能想看看Haxe。它输出更快的字节码,允许低级别的内存访问,并允许作为只读实例变量(编译时功能),这允许实现不变性而无需每个字段访问调用。还有很多其他原因可以使用Haxe,但我会留给你找出答案。


0
投票

我会说在AS3中使用不可变的代表性能敏感应用程序中快速变化的值是一个糟糕的组合,是的。 AS3不是最快的环境,让它处理3D意味着压缩性能。要求它为原语值的每次变化创建一个新对象可能会遇到麻烦。

© www.soinside.com 2019 - 2024. All rights reserved.