
Личные наблюдения по оптимизации
Список всего что я нашел (столкнулся) в процессе написания игры.
(Будет пополняться, буду благодарен если кто нибудь прокомментирует о причинах таких поведений)
1.
Если делаем так, получаем рост памяти с последующей периодической очисткой.
Если так, то получаем прямую и никаких скачков
2.
Если передавать delta:Number, получим то же что и в п.1
Если передавать delta:int, все отлично скачков нет
UPD Здесь суть не в типе (можно и number главное чтоб было целое, а не дробное), а в том что передаем не delta=0,033, например, а delta=33 и уже по месту *0,001.
UPD2
Тест пункта 2.
Space — переключение (delata:INT или delata:Number), UP- добавить 500 об, DOWN — удалить 500.
megaswf.com/serve/1187366
(Будет пополняться, буду благодарен если кто нибудь прокомментирует о причинах таких поведений)
1.
Если делаем так, получаем рост памяти с последующей периодической очисткой.
point.x += numberCordX + _rtX;
Если так, то получаем прямую и никаких скачков
point.x += Number(numberCordX + _rtX);
2.
Если передавать delta:Number, получим то же что и в п.1
tempObj.update(delta);
Если передавать delta:int, все отлично скачков нет
tempObj.update(delta as int);
UPD Здесь суть не в типе (можно и number главное чтоб было целое, а не дробное), а в том что передаем не delta=0,033, например, а delta=33 и уже по месту *0,001.
UPD2
Тест пункта 2.
Space — переключение (delata:INT или delata:Number), UP- добавить 500 об, DOWN — удалить 500.
megaswf.com/serve/1187366
package TEST
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.ui.Keyboard;
import flash.utils.getTimer;
import utils.SWFProfiler;
public class test extends Sprite
{
private var _deltaINT:int = 0;
private var _deltaNUMBER:Number = 0;
private var _maxDeltaTime:int = 60;
private var _lastTick:int = 0;
private var testINT:Boolean = false;
private var container:ObjectController;
private var _text:TextField;
private var n:int = 500;
private var _testmode:String = "delta:NUMBER (=0.03)";
public function test()
{
if (stage)
init();
else
addEventListener(Event.ADDED_TO_STAGE, addedToStageListener);
}
private function addedToStageListener(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, addedToStageListener);
init();
}
private function init():void
{
SWFProfiler.init(stage, this);
container = new ObjectController();
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
stage.addEventListener(Event.ENTER_FRAME, enterFrameListener);
_text = new TextField();
_text.textColor = 0xCE0000;
_text.x = 640 / 2 - 50;
_text.background = true;
_text.height = 50;
_text.width = 220;
_text.selectable = false;
_text.backgroundColor = 0x000000;
//_text.autoSize = TextFieldAutoSize.LEFT;
_text.text = ("TESTMODE: " + _testmode + "\n" + "Object= " + container.objectsLng) as String;
_text.y = 150;
addChild(_text);
}
private function keyDownHandler(e:KeyboardEvent):void
{
if (e.keyCode == Keyboard.SPACE)
{
testINT = !testINT;
if (testINT)
{
_testmode = "delta:INT (=30)";
}
else
{
_testmode = "delta:NUMBER (=0.03)";
}
}
if (e.keyCode == Keyboard.UP)
{
for (var i:int = 0; i < n; i++)
{
container.add(new Unit());
}
}
if (e.keyCode == Keyboard.DOWN)
{
container.remove(n);
}
_text.text = ("TESTMODE: " + _testmode + "\n" + "Object= " + container.objectsLng) as String;
}
private function enterFrameListener(e:Event):void
{
if (!testINT)
{
_deltaNUMBER = 0.03; //(getTimer() - _lastTick) / 1000;
container.update_Num(_deltaNUMBER);
}
else
{
_deltaINT = 30; //(getTimer() - _lastTick);
container.update_INT(_deltaINT);
}
//_lastTick = getTimer();
}
}
}
package TEST
{
public class ObjectController extends Object
{
public var objects:Array = [];
private var tempObj:Object;
private var i:int = 0ж
public function ObjectController()
{
}
public function add(obj:Object):void
{
objects[objects.length] = obj;
}
public function remove(n:int):void
{
objects.splice(objects.length - n, n);
}
public function update_INT(delta:int):void
{
while (i<objects.length)
{
tempObj = objects[i as int];
tempObj.update_INT(delta);
i += 1;
}
i = 0;
}
public function update_Num(delta:Number):void
{
while (i<objects.length)
{
tempObj = objects[i as int];
tempObj.update_Number(delta);
i += 1;
}
i = 0;
}
public function get objectsLng():int
{
return objects.length;
}
}
}
package TEST {
public class Unit {
public function Unit()
{ }
public function update_INT(delta:int):void
{ }
public function update_Number(delta:Number):void
{ }
}
}
- +4
- Marcus
Комментарии (61)
?
Ну уж точно не из за приведения типов.
Профита, что-то около нуля, только код замусоривать.
www.ant-karlov.ru/TowerDefence9-keshirovanie-obektov.html
В последней демке в профайлере память растет и очищается. Вот от такого избавляет.
Кстати, память течет в дебаг версии плеера или в релизном? А то ведь тут может быть все по разному, включая и сами версии плееров.
Пробовал и с Number только без дробной части, в разы меньше но есть (10-100 кб в сек), при int нет течи вообще. Поэтому лучше передавать целое и умножать.
Можно сделать тест чтобы его можно было запустить на разных компьютерах с разными ОС, браузерами и версиями плееров чтобы говорить наверняка о том, что это проблема Flash, а не какой-нибудь частный случай?
MacOS SL, Safari, Flash Player [release] 10.3.
Эти колебания памяти выглядят страшно на фоне 7-8 мб используемой оперативной памяти, когда же игра разрастется до 60-100 мб используемой оперативной памяти, то эти колебания будут почти не заметны на фоне других более сложных объектов. Иными словами: сборщик мусора во время любой игры будет вызываться в любом случае, вопрос только в том сколько он за одну итерацию вычистит мусора… Если следовать такой логике то прийдется кэшировать все на свете, включая локальные переменные внутри методов — ведь они тоже будут мусором после того как их работа закончилась.
А вообще конечно не приятно что такая проблема существует :(
Если это решение сработает, то это будет намного оптимальнее чем приведение к типу int и обратно.
Но даже если бы было быстрым, кэшировать dt маразм.
public function enterFrameHandler(event:Event):void
{
_delta = (getTimer() — _lastTick);
tempObj.update(delta);
}
класс tempObj
public function update(delta:int):void
{
//потом где надо использовать делаем так например
unit.x+=move*(delta*0.001);
}
Или рассчитывать при целой delta.
Но я не вижу просадки фпс при умножении. (при >1500 юнитов)
flash 11.0.1.152, chrome 14.0.835.202, win 7 64 core 2 duo 2.2GHz
Статья (да собственно и комментарии, и ссылки на другие статьи) очень интересные.
_deltaNUMBER = (getTimer() — _lastTick) / 1000;
В то время как тут деления нету:
_deltaINT = (getTimer() — _lastTick);
Но зато в Unit::update_INT пусто, хотя должно быть одно лишнее умножение на 1000 для КАЖДОГО объекта за один глобальный update.
Но память скачет в обоих случаях всех тестов одинаково. На моей ЭВМ, по крайней мере.
На практике я не вижу с моим кол-ом объектов просадки из-за лишних умножений. А вот из-за передачи Number вижу что происходит с памятью.
у меня при INT на 4кб вверх вниз, раз в несколько сек. (что не заметно почти даже тут), а при Number пила вверх\вниз с размахом в 1мб.
Только при Number у меня по 4Мб скачки.
Чей-то Number пилит там ;D
В игре у себя тестил на 1630 объектах (у них дофига всякой логики у каждого внутри) + еще всякие вспомогательные менюшки/панельки жрут. Это нашлось случайно )), при оптимизации визуализации. Просадки по фпс при очистке не большые, но лучше перестраховаться. Плюс я стараюсь каждый фпс вытянуть лишний, с таким кол-ом юнитов всегда пригодиться. Когда память в виде прямой, а не скачет туда сюда мне как-то легче спиться )).
Любая оптимизация имеет смысл при достижении пределов флеша, 150 объектов держит у меня и в векторе, без всяких оптимизаций вообще и пофиг как там скачет память. Вот только мне надо в 10 раз больше и без различных шаманств уже не обойтись.
В байткоде вызовы с Number и int ничем не отличаются.
При этом собрав тест с вызовом функции с Number-аргументом на 1М итераций, каких-то «видимых» колебаний вообще не показывает, вот такой код выполняется по энтерфрейм:
Все дело в строчке objects[i].update_Number(delta), без нее нет скачков (но нам то надо передать), т.е. скачки начинаются на передаче из ObjectController в Unit.
на несколько десятков килобайт дребезжит, но это нормальная ситуация для такого количества вызовов.
Создаются новые объекты?