Tag Archive for 'Оптимизация'

va.lent.in

vИтак, представляю на суд общественности бету моего последнего персонального проекта. Счетчик FPS еще повисит какое-то время. http://va.lent.in.

Я часто загораюсь всякими странными навязчивыми идеями, вот так и с доменом lent.in и далее сайтом на нем. Сайт я делал долго и по кусочкам. В какой-то момент надо было эти кусочки собрать и первая версия получилась каким-то уж совсем abomination. Пришлось перекраивать и перестраивать концепцию.

Эффект IDE я показывал уже давно, и вдохновением для него послужил некоторый реальный продукт и некоторый еще не вышедший продукт. И вот я подумал, а почему бы сайт программиста не сделать в виде кода. Ага! И код будет на фоне печататься руками в темной комнате. Сеттинг был положен и какое-то время я прикидывал возможные исполнения.

Я люблю такие проекты, где после завершения можно написать целый список того, чему ты за этот проект научился.

Сперва, этот IDE изнутри весьма забавен и очень легко конфигурируем. Все синтаксические конструкции из базовых блоков задаются через XML, можно задавать цвет, вложенность любых блоков в любые конструкции. Узким местом оказался список используемых блоков, но что-то добавлять туда мне пришлось всего несколько раз и в самом начале. Очередь печати букв и создания конструкций по шаблонам задается в определенный момент и потом в течение какого-то время выполняется. Для адресации блоков сделал разбор адресов, который вышел весьма кривым, но по крайней мере не медленным. В общем, сама по себе поделка получилась интересная.

Дальше, я получил скилл работы в After Effects, где я собрал и отредактировал видео ряд сайта. Получилось, конечно, не идеально, но мне очень нравится. Особенно, если видеть исходный материал. Ну и стоит упомянуть, что нормально снять-то получилось раза с пятого.

Интересный “эффект подергивания” со смещением RGB каналов претерпел много серьезных изменений. В самом начале, он был на AS, полностью все изображение (в данном случае код рисуется в BitmapData) так красивенько искривлялось, но уже на немного больше небольшого объема текста начинало все ужасно тормозить. Тормозило даже подергивание всей картинки без особого искривления. Сделал тот же эффект на Pixel Bender, но честно говоря на больших картинках особо прироста производительности не заметил. В итоге получился интересный компромисс — маленькими участками (20 пикселей в высоту) сдвигается изображение в рандомной точке со сползанием вверх/вниз. Довольно забавно и гораздо менее ресурсоемко.

Эффект перелистывание картинок тоже мне нравится, хотя и косячит.

Кучу мелких штучек никто поди и не заметит, но на каждую из них ушло тоже солидно времени. Например, для серой линии маленьких картинок, которая идет от квадратного периметра с маленьким кружочком до определенного блока, пришлось вспомнить много геометрии. В итоге риалтайм пересчет пересечений, и кружочек ползет идеально по периметру откуда бы линия не “росла”. Позже, я подумал, что проще было бы сделать это масками (скорее всего), но я не стал уже экспериментировать.

Но, самым важным полученным опытом я считаю знакомство с Flex Profiler. Я с ним просидел не один день, высматривая наплодившиеся объекты и вызываемые функции. Я обычно всегда задумываюсь о производительности и стараюсь что-то оптимизировать. Но на этот раз оптимизация прошла какого-то вселенского масштаба. К концу проекта, новые компоненты уже писались, учитывая весь прошлый опыт, и оптимизировался каждый символ. Код, правда, от этого получился весьма зубодробительный. Инлайны, фактори объектов, всякие ухищрения по ускорению просчета и отрисовки. Конечно же, я сильно уперся в возможности плеера. Фулскрин видео и постоянная перерисовка почти всего экрана отжирают сразу чуть ли не 70% используемого процессорного времени. Все, на что я раньше не обращал внимание, дало о себе знать сразу же. ФПС за ФПСом я отвоевывал у чертового плагина. Я перестал доверять флэшовой событийной модели. Оно толпу событий перекидывает туда-сюда очень уж медленно. Отказавшись от огромного числа лишних событий, удалось поднять производительность заметно выше.

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

Теперь исправить пару новых багов, и можно наслаждаться проделанной работой в полной мере.

Удобство приходит не одно

Такие дела. Что удобно, то не безопаснообычно не быстро.

Ради простецкой анимации хочется тупо написать две строчки:

tf.x = 4;
TweenLite.to( tf, Symbol.DURATION, {x: 0} );

При этом, когда таких анимаций происходит очень много и очень часто, создание для каждой экземпляра TweenLite приводит к сильным потерям в скорости. Я смотрел, у меня за несколько минут понасоздавалось и поубивалось 20000 экземпляров. Это к вопросу.

Приходится брать себя за задницу и писать следующее:

tf.x = 4;
addEventListener( Event.ENTER_FRAME, handlerEffect );

private function handlerEffect( evt: Event ): void
{
  if ( tf.x == 0 )
  {
    removeEventListener( Event.ENTER_FRAME, handlerEffect );
    return;
  }

  tf.x -= .5;
}

Objects pool

Продолжая тему оптимизации, хочу затронуть применение пулов объектов.

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

Другой способ — objects pool, объекты непосредственно не убивать, а скидывать в хранилище. При этом при запросе нового объекта, отдавать почищенный старый.

Простой тест дает убедиться, что данный способ позволяет (как минимум на моей машине) в 4 раза уменьшить накладные расходы на создание новых экземпляров класса. При этом требует совсем незначительных изменений в коде. Просто создавать не через new, а через некоторый Factory, пусть даже статический метод того же класса. И удаляя, тоже его оповещать.

public class TestClass extends Sprite
{
  private var _char: String;
  private var tf: TextField;
  private static var available: Array;

  public function TestClass( char: String )
  {
     super();
     _char = char;
     tf = new TextField();
     addChild( tf );
     tf.text = char;
  }

  public function set char( value: String ): void
  {
    _char = value;
    tf.text = _char;
  }

  public static function createObject( char: String ): TestClass
  {
    if ( !available )
    available = [];

    var o: TestClass;
    if ( available.length > 0 )
    {
      o = available.pop();
      o.char = char;
      return o;
    }else{
      return new TestClass( char );
    }
  }

  public static function deleteObject( obj: TestClass2 ): void
  {
    if ( !available )
    available = [];

    available.push( obj );
  }

}

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

Распечатать и приклеить на лоб.

По мотивам поста сделал и распечатал себе картинку.

as3optimization

Похожая со свойствами TweenLite висит у меня в офисе. Новичкам будет уж точно очень полезно.

Много об оптимизации

Последние пару дней я посвятил некоторым тестам производительности, о чем писал, например, тут.

И вот сегодня Grant Skinner публикует отличнейший материал. Многого не знал. Рекомендую всем AS3 разработчикам!

Скорость vs OOP

Как выяснилось, AS3 компилятор необычайно туп и не проводит никаких оптимизаций. Ну, может быть какие-то совсем простые.

Довелось мне посидеть оптимизировать небольшую библиотечку, на которой стоят все мои остальные. В ней происходит разбор XML, да через e4x, но все же разбор и создание некоторых объектов на основе этого XML. При этом, проходят некоторые проверки функциями isЧто-то(XML), которые представляют собой всего 1-3 строчки. Вызываются такие функции для каждого элемента, то есть часто.

А теперь смотрим тест.

var t: int;
var v: int = 5;
var w: int = 6;

for ( var i: int = 0; i < 5; i++ )
{
  t = getTimer();
  for ( var j: int = 0; j < 1000000; j++ )
  {
    var k: Boolean = func( v, w );
    //var k: Boolean = v > w;
  }
  trace( getTimer() - t );
}

private function func( v: int, w: int ): Boolean
{
  return v > w;
}

Выполняется у меня под 400ms.

Если раскомментировать вторую строку и закомментировать первую, то время падает до 90-100ms. В 4 раза. Полмиллисекунды тут, полмиллисекунды там и уже набираются десятки. Вспомним, что при 30fps на 1 кадры выделяется 33 миллисекунды. А ведь еще столько кода нужно выполнить.

Более того,

var t: int;
for ( var i: int = 0; i < 5; i++ )
{
  t = getTimer();
  for ( var j: int = 0; j < 1000000; j++ )
  {
    var k: int = value;
    //var k: int = _value;
  }
  trace( getTimer() - t );
}

public function get value(): int
{
  return _value;
}

та же фигня. Геттер по сути та же функция с теми же проблемами.

Я пошел и заменил все вызовы этих функций на сами функции. Любой программист знакомый с ООП сочтет меня дураком, потому что я копирую код. Пойдет на форум и напишет какой я тупой и не знаю ООП. Запихнет все эти скопированные куски в функции и будет горд собой. Вот он и вопрос. Скорость или ООП? По крайней мере пока компилятор остается таким говнистым, что сам не соображает как лучше сделать.

“Как надо делать компиляторы…”

… , — сказал Joa Ebert, когда пришел в гости к Adobe. И действительно, насколько я понял, Joa делает оптимизирующий исходный байткод компилятор для AS3. У меня опыта оптимизирующих компиляторов ноль, но то, что описано на сайте имеет смысл. Надеюсь, у него эта затея пойдет дальше и возможно реализация примется Adobe в следующую версию mxmlc.