Зачем кешировать данные? Для производительности!

Share Button

Вчера общался со старым другом, у которого внезапно оказался портал детских развивающих игр с платным доступом. Он спрашивал совета об оптимизации флеш игры. На его мнение, в одном месте, возникали жуткие тормоза при условии, что много объектов на экране. Не буду судить об авторе тормозного кода игры, но после небольших советов FPS значительно вырос.

В ENTER_FRAME стояли функции перевода градусов в радианы, определение размеров объекта width и height и еще кучу всего математического, что вызвало первое желание влезть в чужой продукт.

Был код в одном блоке, выполняющийся в цикле for для кучи объектов:

for ( var i : int = 0; i < lvl.numChildren; i ++ ) {
var block : BlockMovie = lvl.getChildAt(i) as BlockMovie;
block.direction = angle * Math.PI / 180;
block.maxX = background.width - block.width/2;
if ( block.maxX > container.getChildByName("flag").width ) {
...

И эта «писанина» в моих глазах отобразилась «как гром среди ясного неба». Я не стал лезть в первопричины и пошел с конца. На что я обратил внимание:
1) каждый раз выполняется метод lvl.numChildren для подсчета количества детей и возврата их числа.
2) метод lvl.getChildAt(i) выдающий новый объект-ссылку с результатом.
3) Math.PI / 180 — расчет формулы на лету.
4) background.width — block.width/2 в этом случае у него объекты имеют всегда статический размер. Зачем каждый раз напрягать процессор — я не понимаю.
5) container.getChildByName(«flag») поиск объекта по имени и его возврат.
6) .width определение ширины объекта из предыдущего шага.

Давайте рассмотрим, как это работает:
1) Одно дело узнать количество вложенных объектов и закончить операцию и другое дело выполнять ее постоянно каждый раз в ENTER_FRAME. Это совсем не «выгодно» для процессора. Вместо этого был создан метод:

private function addBlock():void{
 var block : BlockMovie = new BlockMovie();
 lvl.addChild(block);
 blockList.push ( block );
 blockListCount++;
}

Т.е. после вызова этого метода у нас добавляется его блок в Vector. и переменная с количеством блоков увеличивается на 1. Конечно, можем и не делать blockListCount++; и обойтись в дальнейшем blockList.length для получения длинны — но уверяю Вас. Когда начинаете писать под мобильные устройства — начинаете экономить каждую копейку процессорного времени и памяти. А хорошие привычки — это хорошие привычки.

2) Теперь избавимся и от строки var block : BlockMovie = lvl.getChildAt(i) as BlockMovie;. Она имеет уж очень неприлично много инструкций, когда «ну тут оптимизировать уже больше нечего, помоги что-то придумать.». Учитывая, что у нас теперь есть список с блоками — мы можем напрямую обращаться в него. И этой строки вовсе не будет.

3) Различные constant folding это хорошо, но у нас это не сработает в готовом проекте, который имеет безобразные методы. В последнем компиляторе ASC2.0 (не путайте с AS2.0) была заявлена поддержка автоматического сворачивания констант. Я сам не проверял, по-этому буду думать, что я об этом не слышал. Да и игрушки почти все пишут «по старинке». Так вот — если мы постоянно делаем Math.PI / 180 — зачем нам это надо, если тут ничего не меняется? Вместо этого напишем сразу результат 0.017453292519943295

4) После вопросов меняется ли размер фона и блоков и получении ответа «нет» я был крайне удивлен такой конструкции background.width — block.width/2. Почему бы это предварительно не рассчитать? Мало кто задумывался, как работает getter метод width и height. Он не просто возвращает размер. Он сначала каждый раз делает перерасчет физических размеров. Конечно, когда у нас пару шариков на экране — не заметно. Но когда их много — мы начинаем заставлять процессор напрягаться. Для этого можно ввести систему кеширования width и height в классе. Чтоб у нас возвращался не width, а хотя бы _width. А при изменении размеров — мы будем обновлять _width из width. Т.е. если не было изменения размеров — мы просто возвращаем _width. Все элементарно решается с помощью геттеров, сеттеров и прочих хотдогов. И тем самым CPU не проводит каждый раз новые расчеты. Менять архитектуру кода было «автору» лень — по-этому так и записали _width, _height и ввели новую переменную halfWidth, которая равнялась _width/2 и тоже устанавливалась после обновления размеров автоматически. И там же рассчитали всю строку block.maxX = background.width — block.width/2; и наша переменная maxX стала при инициализации уже знать свои размеры.

5) container.getChildByName(«flag») про это я уже писал выше — большая засада «быстрой» разработки, когда хочется быстрее посмотреть результат. Но, почему-то, многие это так и бросают. Мы же сделали просто ссылку вначале var flag : MovieClip = container.flag;

6) За width я уже писал — операция не сильно, но прожорливая. У flag вообще никогда не меняется в приложении ширина и она равна 32 пикселя. По-этому так и оставим.

На выходе получился оптимизированный код:

for ( var i : int = 0; i < blockListCount; i ++ ) {
blockList[i].direction = angle * 0.017453292519943295;
if ( block.maxX > 32 ) {
...

Конечно, тут есть пути для небольшой шлифовки напильником. Но это лично мне делать было лень и менять архитектуру чужого кода — не совсем благодарное дело. Я оставил это автору. Спустя несколько часов я узнал, что он так и оставил, т.к. «уже получился серьезный прирост FPS!».

В целом, что хотел бы сказать… Имея желание что-либо изменить — можно существенно поднять производительность любого приложения, где осуществляются выделения памяти и математические расчеты для большого числа объектов или операций. Пока одни жалуются на скорость выполнения as3 кода — другие жалуются на руки разработчиков, которые жалуются на скорость виртуальной машины :)

Написать плохой код я люблю и сам и казалось бы, что умничать проще, чем делать. Но когда второй человек проходит по коду первого — продукт становится лишь лучше. Все мы совершаем ошибки :) Главное работать над их устранением.

P.S. Естественно, что VM далеко до идеала. Но если писать код «мне так хочется» — то и работать оно будет «как попало». После небольшой практики на Objective-C мне стало ясно, что многие as3 разработчики вообще не понимают надобности проводить оптимизацию кода. Им проще свою лень списать на виртуальную машину. Ну или «у Вас компьютер слабый», что я слышу регулярно :)

Share Button

This Post Has Been Viewed 593 Times

Добавить комментарий

Ваш e-mail не будет опубликован.

Blue Captcha Image Новый проверочный код

*

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>