Снижаем нагрузку на процессор в Adobe AIR

     
    Снижаем нагрузку на процессор в Adobe AIR

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

    Один из самых простых способов резко снизить нагрузку на ЦП основан на изменении частоты обновлений окна в секунду (Framerate Throttling). В этой статье я объясню метод с framerate'ами и покажу, как наилучшим образом использовать данный метод в Ваших программах.

    Примечание: Чтобы использовать данный метод Вы должны иметь основные понятия об ActionScript'е и программировании на AIR.

    Что же такое замедление framerate'ов (Framerate Throttling)?

    Замедление framerate'ов — это техника, позволяющая контролировать framerate'ы программы, что, в свою очередь, позвляет увеличить её производительность при использовании (Active) и снизить количество потребляемые ресурсов когда она не используется (Idle). Как и в ActionScript 3, разрабочики имеют необычнайно полезную строчку кода в своём распоряжении — Stage.frameRate. Она позволяет изменять количество обновлений окна программы в секунду на лету. В предыдущих версиях ActionScript у нас были существенные проблемы с внедрением этой функции в интегрированную среду разработки (IDE). К счастью, времена изменились и теперь сложные программы не "висят", задерживаясь в памяти компьютера в фоновом режиме.

    Как же использовать Framerate Throttling?

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

    Примечание: производительность замерена на Macbook Pro 2.8 GHz Intel Core 2 Duo с использованием нижеизложенных примеров программного кода и выражена в процентах. Естественно, результаты будут варьироваться в зависимости от конфигурации машины.


    "Новичок"

    Самый простой способ использования методики замедления framerate'ов — NativeApplication Event.ACTIVATE и Event.DEACTIVATE, эти события увеличивают количество framerate'ов когда программа используется (Active) и уменьшает когда она неактивна (Idle). С единичным пустым окном результаты показали нагрузку процессора в 1.8% в режиме использования и 0.4% в режиме простоя. Вы вообще можете установить частоту обновлений окна программы 0.01 или выключить его, тогда программа занимает всего 0.2% от общей нагрузки ЦП, при этом я выяснил, что chrome окна при этом не теряет фокусировки.

    package {
       import flash.desktop.NativeApplication;
       import flash.display.Sprite;
       import flash.events.Event;
     
       public class Application extends Sprite {
          public function Application () {
             __init ();
          }
         
          private function __init ():void {
             NativeApplication.nativeApplication.addEventListener
               (Event.ACTIVATE, __activate__);
             NativeApplication.nativeApplication.addEventListener
               (Event.DEACTIVATE, __deactivate__);
          }
         
          private function __activate__
             ($event:Event):void {
             stage.frameRate = 50;
          }
          private function __deactivate__ ($event:Event):void {
             stage.frameRate = 1;
          }
       }
    }


    "Продвинутый"

    Определённые программы позволяют более эффективно использовать методику замедления framerate'ов. Например, программа, которой необходим какой-то уровень взаимодействия даже в фоновом режиме. Программа, у которой есть прокручиваемый контент (scrollable content) с ссылками, поэтому AIR позволяет мыши прокручивать содержимое программы, когда она активна, что в свою очередь означает, что в текущий момент вам нужно большее значение framerate'ов.

    В этом примере, если программа находится в фоновом режиме, но при этом мышь прокручивает содержимое программы, MouseEvent.MOUSE_WHEEL увеличивает framerate и устанавливает Event.ENTER_FRAME событие, которое уменьшит framerate на половину секунды после прокручивания. В случаях схожих с этим, лучшим выбором будет использование буфера, что позволит не менять каждый раз framerate с каждым прокручиванием, а так же потому, что тогда не нужно будет событие для момента, когда колесо мыши находится в режиме простоя (Idle).


    package {
       import flash.desktop.NativeApplication;
       import flash.display.Sprite;
       import flash.events.Event;
       import flash.events.MouseEvent;
       import flash.utils.getTimer;
     
       public class Application extends Sprite {
          public static const ACTIVE:int = 50;
          public static const INACTIVE:int = 1;
     
          public var active:Boolean;
          public var scrolling:Boolean;
          public var buffer:int;
         
          public function Application () {
             __init ();
          }
         
          private function __init ():void {
            NativeApplication.nativeApplication.addEventListener
            (Event.ACTIVATE, __activate__);
            NativeApplication.nativeApplication.addEventListener
            (Event.DEACTIVATE, __deactivate__);
            stage.addEventListener
            (MouseEvent.MOUSE_WHEEL, __mouseWheel__);
          }
         
          private function __activate__ ($event:Event):void {
             active = true;
             stage.frameRate = ACTIVE;
          }
          private function __deactivate__ ($event:Event):void {
             active = false;
             stage.frameRate = INACTIVE;
          }
          private function __mouseWheel__ ($event:MouseEvent):void {
             if (!active) {
               if (!scrolling) {
                  stage.addEventListener
                    (Event.ENTER_FRAME, __enterframe__);
               }
               stage.frameRate = ACTIVE;
               scrolling = true;
               buffer = getTimer () + 500;
             }
          }
          private function __enterframe__
             ($event:Event):void {
             if (buffer < getTimer ()) {
               stage.frameRate = INACTIVE;
               scrolling = false;
               stage.removeEventListener
                (Event.ENTER_FRAME, __enterframe__);
             }
          }
       }
    }


    "Эксперт"

    Если оптимизация — смысл вашей жизни, то вы можете удивить своих друзей сложными манипуляциями с замедлением числа framerate'ов. (Примечание: это не впечатлит Вашу девушку).

    В моих программах я люблю делать переходы из одного состояния окна в другое плавным и более эстетичным. Поэтому я использую высокое число framerate'ов — 50. К сожалению, такое большое число ведёт к сильной нагрузке на ЦП. Поэтому я устанавливаю framerate 50 только при движении элементов \ окон. После окончания движения, я снижаю количество framerate'ов до 24. Бывают случая, когда загрузчик проигрывает анимацию, в то время когда сама программа находится в фоновом режиме. Загрузчику не нужно 50 fps, поэтому я устанавливаю число framerate равное 5 когда програма видна в фоновом режиме и ставлю 1 когда не видна.

    Примечание: для этого примера я использую animate() для вызова в начале каждого движения. Возможно, Вам захочется интегрировать свой собственный замедлитель fps в ваш tweet-движок, так что вам более не нужно будет вручную вызывать animate().

    package {
       import flash.desktop.NativeApplication;
       import flash.display.Sprite;
       import flash.events.Event;
       import flash.utils.getTimer;
     
       public class Application extends Sprite {
          public static const ANIMATING:int = 50;
          public static const ACTIVE:int = 24;
          public static const INACTIVE_VISIBLE:int = 5;
          public static const INACTIVE_INVISIBLE:int = 1;
         
          public var active:Boolean;
          public var animating:Boolean;
          public var buffer:int;
         
          public function Application () {
             __init ();
          }
         
          private function __init ():void {
            NativeApplication.nativeApplication.addEventListener
            (Event.ACTIVATE, __activate__);
            NativeApplication.nativeApplication.addEventListener
            (Event.DEACTIVATE, __deactivate__);
          }
         
          public function activate ():void {
             if (!animating) {
               stage.frameRate = ACTIVE;
             }
          }
          public function deactivate ():void {
             if (!animating) {
               stage.frameRate = (stage.nativeWindow.visible) ?
                  INACTIVE_VISIBLE : INACTIVE_INVISIBLE;
             }
          }
          public function animate ($duration:int = 1000):void {
             stage.frameRate = 50;
             buffer = getTimer () + $duration;
             animating = true;
            
             if (!animating) {
               stage.addEventListener (Event.ENTER_FRAME, __checkBuffer__);
             }
          }
         
          private function __activate__ ($event:Event):void {
             active = true;
             activate ();
          }
          private function __deactivate__ ($event:Event):void {
             active = false;
             deactivate ();
          }
          private function __checkBuffer__ ($event:Event):void {
             if (buffer < getTimer ()) {
               stage.removeEventListener
               (Event.ENTER_FRAME, __checkBuffer__);
               animating = false;
               if (active) {
                 activate ();
               } else {
                 deactivate ();
               }
             }
          }
       }
    }


    Замедление framerate'ов лишь малая глава в оптимизации вашей программы, написанной на AIR. Но это основы оптимизации, и применение данной методики приведёт Вас к оптимальному использованию ресурсов компьютера при написании программ. Согласитесь, никто не любит громоздкие и неудобные программы.

    Об авторе

    Джонни — опытный дизайнер команды XDCE Adobe. До присоединения к Adobe, он разрабатывал программы на AIR такие как DestroyFlickr и DestroyTwitter в своё свободное время для развлечения. Так же у него есть собственный блог, "Destroy Today", который стал возможностью для него делиться своими находками в программировании с другими. Довольно странно, ведь Джонни получил Бакалавра в Колледже искуств при институте Мэриленда.
     
    Оригинальный текст на английском языке тут:
    www.adobe.com/devnet/air/flex/articles/framerate_throttling.html

    Комментарии 19

      –1
      Очень кстати, спасибо.
        +6
        __deactivate__ < — Зачем так функции называть! ну хоть deactivateHandler но точно не _____deactivate___!
          0
          потомучто

          >Джонни — опытный дизайнер команды XDCE Adobe
          >Джонни получил Бакалавра в Колледже искуств при институте Мэриленда
            0
            Да, точно опытный дизайнер — не заметил…
          0
          Перевод слишком косноязычный, пойду читать оригинал ;)
            0
            Даешь другие главы про оптимизацию!
            P.S. А что на веб приложениях такое не работает?
              +1
              Такой подход особенно необходим для маков. Там cpu usage для AIR приложений почему-то особенно страдает. Может это как-то связано с политикой apple в отношении Flash приложений?
                –5
                Немного оффтоп, но есть ли у кого-нибудь книга «AIR 1.5 Coоkbook» в эл. виде?
                  0
                  Ссаным тапком надо бить за попытку прямого доступа к stage. EnterFrame ловится откуда угодно.
                    0
                    В чем собственно проблема? Можно аргументы?
                      0
                      Проблемы начнутся, когда ваш код попытаются использовать частью другого приложения. Незабываемые впечатления.
                        0
                        не до конца понял. EnterFrame он и в африке enterframe. Какая разница кого слушать и какое приложение является родителем?
                          0
                          Не совсем так. Очень вероятна ситуация, когда вам не дадут ссылку на stage и ваш класс посыпется null эксепшенами в самых разных неожиданных местах. В примере выше это будет сразу-же, как кто-то попытается сделать new Application(); и придется переписывать класс, причем не понятно какой. То-ли тот который вызывает, что-бы не сломать готовый функционал, то-ли текущий, в надежде не упустить деталей реализации.
                            0
                            stage доступен для всех обьектов, находящихся в дисплей листе. Если кто-то пытается обратиться к stage обьекта не в дисплей листе — это проблема кривизны рук разработчика, а не подхода прямого доступа к stage.
                              –1
                              Имхо не совсем так. Если мы имеем SWF в SWF, то в дело вступают ограничения безопасности, и помоему они не дадут дочернему SWF обратится к stage, хоть он и в дисплей листе. Но в такой ситуации будут проблемы с любым кросскриптингом.
                                0
                                К самому stage очень даже дадут. Ограничения безопасности ограничат доступ к некоторым его методам и атрибутам. Но и это решаемо в рамках политик безопасности, а так же выравниванием кривизны рук разработчика. И не имеет отношения к вопросу «прямой доступ к stage — это плохо».
                    0
                    странно, почему еще это на уровне платформы не сделано и такие костыли надо приделывать самому разработчику.
                      0
                      Как-то странно всё это… По-моему проще ничего не делать по EnterFrame и не использовать MovieClip. Перерисовывать самостоятельно Bitmap/ делать вычисления в НУЖНЫЙ момент. Если ничего не изменяется, то ничего и не перерисовывается и без танцев с бубнами (по умолчанию). Включите в дебаговом плеере Show Redraw Region увидьте, что именно у вас перерисовывается — сделайте так чтоб не перерисовывался).
                        0
                        Мы прооптимизировали 5pm TimeTracker widget
                        (http://www.5pmweb.com/feature_timetracker.php) после жалоб от
                        Мак-клиентов.

                        Для framerate throttle есть готовый класс:
                        www.gskinner.com/blog/archives/2009/05/idle_cpu_usage.html

                        Кроме этого снизили framerate в активном положении и переписали
                        onEnterFrame евент на Timer (с низкой частотой вызовов).

                        Есть хорошая статья про оптимизацию AIR виджетов:
                        blogs.adobe.com/air/2009/05/performance_tips_for_adobe_air.html

                        Результат: CPU нагрузка снизилась в 10 раз в неактивном состоянии и в
                        2-3 раза в активном.

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

                        Самое читаемое