Таймеры AS3 по сравнению с производительностью ENTER_FRAME

Вы можете установить контент как атрибут данных, а затем использовать селекторов атрибутов , как показано здесь:

/* Select every cell containing word "male" */
td[data-content="male"] {
  color: red;
}

/* Select every cell starting on "p" case insensitive */
td[data-content^="p" i] {
  color: blue;
}

/* Select every cell containing "4" */
td[data-content*="4"] {
  color: green;
}
Peter male 34
Susanne female 14

Вы также можете использовать jQuery для простой установки атрибутов содержимого данных:

$(function(){
  $("td").each(function(){
    var $this = $(this);
    $this.attr("data-content", $this.text());
  });
});

11
задан Makram Saleh 9 July 2009 в 09:38
поделиться

3 ответа

, возможно, было бы разумнее иметь только один работающий таймер в этом отношении ... насколько я знаю, для работающего таймера нужен целый поток ... to put it in pseudo-code, the Timer thread's main code is something like that ...

while (input.isEmpty()) {
    wait(interval);
    output.add({timerId:thisId, tickId: tickId++});
}

output being a dequeue the main thread (which executes the ABC) checks every now an then ... having many Timers, you will have many threads, which is an unnecessary overhead ... also, for every event, the message sent from the timer to the main thread needs to be popped of the deque, which is expensive, since it has to be thread safe ... and then the corresponding timer has to be found, a timer event has to be created (allocation is also quite costy) and then dispatched, which also is a matter of multiple calls ...

so try to have ONE timer, or use setInterval ... also, consider, that the Event model in flash is quite nice, but expensive ... it is used for decoupling, to ensure a nice architecture ... for the same reason, it is not good for performance critical situations ... once again, dispatching an event is expensive ...

i've made a little class, that is a little more manual (it's just to make my point, although it oculd be used in theory):

package  {
    import flash.utils.*;
    public class Ticker {
        //{ region private vars
            private var _interval:int;
            private var _tick:uint = 0;
            private var _tickLength:Number;
            private var _callBacks:Dictionary;
        //} endregion
        public function Ticker(tickLength:Number = 0) {
            this.tickLength = tickLength;
            this._callBacks = new Dictionary();
        }
        //{ region accessors
            /**
             * the current tick
             */
            public function get tick():uint { return _tick; }
            /**
             * the tick length. set to a non-positive value, to stop ticking
             */
            public function get tickLength():Number { return _tickLength; }
            public function set tickLength(value:Number):void {
                if (this._tickLength > 0) clearInterval(this._interval);
                if ((this._tickLength = value) > 0) this._interval = setInterval(this.doTick, value);
            }       
        //} endregion
        /**
         * add a callback, to be called with every tick
         * @param   callback function (tick:int):*
         */
        public function addCallback(callback:Function):void {
            this._callBacks[callback] = callback;
        }
        /**
         * removes a callback previously added and returns true on success, false otherwise
         * @param   callback
         * @return
         */
        public function removeCallback(callback:Function):Boolean {
            return delete this._callBacks[callback];
        }
        /**
         * executes a tick. actually this happens automatically, but if you want to, you can set tickLength to a non-positive value and then execute ticks manually, if needed
         */
        public function doTick():void {
            var tick:uint = this._tick++;//actually, this is only superspicion ... amazingly, this makes no difference really ... :D
            for each (var callback:* in this._callBacks) callback(tick);
        }
    }
}

it performs quite well ... here a benchmarking class (you should be able to simply use it as document class in a fla, if you use CS3/CS4):

package {
    //{ region imports
        import flash.display.*;
        import flash.events.*;
        import flash.sampler.getSize;
        import flash.system.System;
        import flash.text.*;
        import flash.utils.*;   
    //} endregion
    public class Main extends MovieClip {
        //{ region configuration
            private const timers:Boolean = false;//true for Timer, false for Ticker
            private const delay:Number = 500;
            private const baseCount:uint = 10000;//base count of functions to be called
            private const factor:Number = 20;//factor for Ticker, which is a little more performant     
        //} endregion
        //{ region vars/consts
            private const count:uint = baseCount * (timers ? 1 : factor);
            private const nullMem:uint = System.totalMemory;//this is the footprint of the VM ... we'll subtract it ... ok, the textfield is not taken into account, but that should be alright ... i guess ...
            private var monitor:TextField;
            private var frameCount:uint = 0;
            private var secCount:uint = 0;      
        //} endregion
        public function Main():void {   
            var t:Ticker = new Ticker(delay);
            var genHandler:Function = function ():Function {
                return function (e:TimerEvent):void { };
            }
            var genCallback:Function = function ():Function {
                return function (tick:uint):void { };
            }
            for (var i:uint = 0; i < count; i++) {
                if (timers) {
                    var timer:Timer = new Timer(delay, 0);
                    timer.addEventListener(TimerEvent.TIMER, genHandler());
                    timer.start();                  
                }
                else {
                    t.addCallback(genCallback());
                }
            }
            this.addChild(this.monitor = new TextField());
            this.monitor.autoSize = TextFieldAutoSize.LEFT;
            this.monitor.defaultTextFormat = new TextFormat("_typewriter");
            this.addEventListener(Event.ENTER_FRAME, function (e:Event):void { frameCount++ });
            setInterval(function ():void { 
                    monitor.text = "Memory usage: " 
                        + groupDidgits(System.totalMemory - nullMem) 
                        + " B\navg. FPS: " + (frameCount /++secCount).toPrecision(3) 
                        + "\nuptime: " + secCount + "\nwith " + count + " functions"; 
                }, 1000);
        }
        private function groupDidgits(n:int,sep:String = " "):String {
            return n.toString().split("").reverse().map(function (c:String, i:int, ...rest):String { return c + ((i % 3 == 0 && i > 0) ? sep : ""); } ).reverse().join("");
        }
    }
}

on my machine, with 60 FPS targetet, i get an average FPS of 6.4 (after 3 minutes) and 10-14 MB memory usage (fluctuation comes from the fact that TimerEvent objects need to be garbage collected) for 10000 functions being called with timers ... using the other class, i get 55.2 FPS with 95.0 MB memory usage (very constant, fluctuations are unter 1%) with 200000 functions being called directly ... this means, at factor 20 you get a framerate that is 9 times higher, and you use only 8 times the memory ... this should get you an idea of how much footprint a timer creates ...

this should get you a rough idea, in which direction to go ...

[edit] i've been asked, why i use private vars ... matter of philosophy ... my rule: never ever let anyone from outside change the state of your object directly ... imagine Ticker::_tickLength was protected ... someone subclasses it, and writes to that variable ... with what effect? the value of Ticker::tickLength will be different from the interval length ... i don't really see an advantage ...

also, private fields are only valid in a class ... which means anyone can redefine them within subclasses without any collisions ...

if i think, that subclasses should have a protected way to take effect on the state defined in the superclass, i make a protected setter ... but still, i can react ... i can change/validate/clamp the value, throw argument and range errors at will, dispatch events, and so on ... if you write a class, you yourself are responsible for maintaining the integrity of its state and the effects on its behaviour ...

do not expose the inner workings of your class ... you may need to change them, breaking dependant code ... and also: subclassing is hugely overrrated ... :)

so that's why ... [/edit]

greetz

back2dos

8
ответ дан 3 December 2019 в 08:05
поделиться

, если вы еще не используете библиотеку tween, я бы посмотрел на tweenlite или tweenmax. он включает в себя вызываемый таймер с задержкой, а также группировку промежуточных кадров. он имеет отличную производительность и прост в использовании.

взгляните на тесты производительности

http://blog.greensock.com/tweening-speed-test/

Джош

1
ответ дан 3 December 2019 в 08:05
поделиться

Я бы рекомендовал использовать ENTER_FRAME в качестве основной «галочки» для вашего игрового движка. ENTER_FRAME точно соответствует частоте кадров Flash Player, которая является истинной максимальной частотой кадров, с которой будет работать ваш код. Таймеры и т. Д. Являются всего лишь приблизительными и не могут выполняться быстрее, чем ENTER_FRAME.

На самом деле, хотя я изначально использовал таймеры для всех моих вещей, я постепенно отхожу от них из-за проблем с псевдонимом. Если вы установите таймер на 30 кадров в секунду, но Flash Player в конечном итоге будет работать со скоростью 15 кадров в секунду, тогда таймер в конечном итоге отправит событие TIMER дважды между событиями ENTER_FRAME. Если эти события TIMER приводят к дорогостоящему коду (что они могли бы сделать, если бы это тик вашего игрового движка), то это может привести к снижению фактической частоты кадров игрока (потому что теперь вы re, дважды помеченный для ENTER_FRAME).

Таким образом, таймер хорош, если у вас есть что-то, что вы хотите запускать периодически, но для запуска чего-либо, близкого к фактической частоте кадров SWF, я бы рекомендовал просто использовать частоту кадров SWF и регулировать вашу логику по мере необходимости.

Один из подходов - вычислить временные дельты для каждого ENTER_FRAME. Если у вас есть логика, основанная на времени, это лучший подход. Другой подход, если ваш SWF предполагает фиксированную частоту обновления (например, код на основе таймера), заключается в вызове метода тика вашей игры, если и только если вы превысили временную дельту для любого заданного ENTER_FRAME.

I не рекомендовал бы делать два тика на ENTER_FRAME, если вы отстаете (или вы окажетесь в той же ситуации, что и таймеры). В определенный момент ваша игра должна замедлиться, иначе она станет неиграбельной (из-за того, что дельты становятся слишком большими). Выполнение более одного тика на ENTER_FRAME, когда вы уже замедлились, только замедлит вас еще больше. Пользователи могут лучше справиться с замедленным геймплеем, чем с пропуская геймплей.

6
ответ дан 3 December 2019 в 08:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: