3 Replies Latest reply on Jan 5, 2011 9:35 AM by Flex harUI

    Performance best practices

    FredericCox Level 1

      Hello everyone,

       

      I'm working on quite a large application which has some core functionality in a shell application and uses modules to load other parts of the application (a content managment system).

       

      Using Flex 4 we tried to seperate logic and views as much as possible and we use .css styles which are compiled to .swf to load a style at runtime.

       

      I'm now trying to (drastically) improve the performance because some parts are really slow. We improved some things by using callLater, deferred creation, etc.. so we split the code execution accross multiple frames so the perceived performance is better.

       

      What I'm mostly curious about are these two things:

       

      - I noticed one of our developers has used this code in a lot of modules (var timer:Timer = new Timer(100)) which means this timer is started when the module has been created and so a timer will tick every 100ms. Also this code is appearing in the shell, so a lot of timers are ticking but no listeners are added yet (this is happening based on a parameter but the instantiation of timers is always happening so we need to avoid that) but how does something like that affect performance? Is this neglectible, i'm unable to find these kinds of information on the net.

       

      - Secondly we use a LanguageManager class which holds a XML object containing a translation file, this XML object is referenced in a lot of places like:

       

      <s:Label id="alert"
                   text="{LanguageManager.getManager().getText(LanguageManager.getManager().texts, 'No data found')}"
                   verticalCenter="0"
                   horizontalCenter="0"/>

       

      In LanguageManager that texts object is set as bindable

      [Bindable]
              public var texts:XML;

       

      Is this heavy for performance and/or memory? What does bindable do internally? If we would to disable that would this impact performance in a good way?

       

      What are the most common pitfalls for developing Flex 4 applications according to you? We developped and implemented features and bugs on a fast PC/Mac and now on slower machine we face these problems so need to optimize it.

       

      Thanks for your help and tips!

        • 1. Re: Performance best practices
          UbuntuPenguin Level 4

          There are alot of performance tips when developing in Flex.  The first being unnecessary databinding.  Let's start with a simple class called CoffeeShop.

           

          [Bindable]

          public class CoffeeShop (cause that's where I am right now )

          {

               public var servers:ArrayCollection;

               public var customers:ArrayCollection;

           

               //Not really sure if this is how you declare an arraycollection inline, but you get the picture

               public var coffeeFlavors:ArrayCollection = new ArrayCollection({"Sumatra" , "Kenyan" , "Colombian"});

           

               public var serversOnDuty:ArrayCollection;

               public var address:String;

               public var name:String;

               public var shopFloorPlan:FloorPlan;

          }

           

          Now there are few improvements that could be made to this class.  Since we have the [Bindable] tag before the class, it makes EVERY property of the class bindable.  Shortly put, anytime any property of this class is changed, anything bound to the class receives a PropertyChangeEvent and updates even if the property that is being watched has not changed.  For example, a Label watching the "name" of the coffeeshop will update whenever the "address" is changed through a TextInput component. Unacceptable...

           

          The smarter option would be to move the [Bindable] tag to the individual properties of the class, like so.

           

          public class CoffeeShop (cause that's where I am right now )

          {

               [Bindable] public var servers:ArrayCollection;

               [Bindable] public var customers:ArrayCollection;

               //Not really sure if this is how you declare an arraycollection inline, but you get the picture

               [Bindable] public var coffeeFlavors:ArrayCollection = new ArrayCollection({"Sumatra" , "Kenyan" , "Colombian"});

           

           

               [Bindable] public var serversOnDuty:ArrayCollection;

               [Bindable] public var address:String;

               [Bindable] public var name:String;

               [Bindable] public var shopFloorPlan:FloorPlan;

          }

           

          Now when a property updates, only things bound to that property have to update.  But hold on, more optimizations await us.  Let's assume some of the properties are not going to change throughout the execution of the program, something you have to decide on.  For instance, our coffeeshops hate new flavors like I hate singletons, so we can assume coffeeFlavors will stay the same as will the floorplan.  Well, why use an arraycollection which has the databinding overhead associated with it ?  So we can change the arraycollection to an array, and remove the [Bindable] tag from the coffeeFlavors and the shopFloorPlan.

           

          public class CoffeeShop (cause that's where I am right now )

          {

               ...

               //Not really sure if this is how you declare an arraycollection inline, but you get the picture

               public var coffeeFlavors:Array= new Array("Sumatra" , "Kenyan" , "Colombian");

               ...

               public var shopFloorPlan:FloorPlan;

          }

           

          Going somewhere, NOT SO FAST, there is still more work to do.  Lets assume we want to find out which coffeeshops a server works at, assuming they work at more than one.  Some programmers would add a method like this to the coffeeshop class.

           

          ...

          public function serverWorksHere( server:Server ):Boolean

          {

              for each( var coffeshopServer:Server in servers )

              {

                    if( coffeshopServer.serverId == server.serverId )

                    {

                          return true;

                    }

              }

              return false;

          }

          ...

           

          And in the manager or presenter, would have a function like this

           

          public function get serversShopList( server:Server ):ArrayCollection

          {

             var returnCollection:ArrayCollection = new ArrayCollection();

             for each( var coffeeShop:CoffeeShop in arrayOfCoffeeShops )

            {

                if( coffeeShop.serverWorksHere( server ) )

                {

                     returnCollection.addItem( coffeeShop );

                }

            }

          }

           

          As we can see, the first method of finding whether or not a server works at a particular shop is O(n*m).  Scaling with the number of servers at the shops (n) and the number of coffeeshops we have to iterate over (m).  This could get real ugly real fast.  A better way is to use "getItemIndex(object)" on the ArrayCollection, if it returns -1 return false, otherwise return true;

           

           

          //There is a whole world of other methods on interfaces implement by the ArrayCollection that can be used too ...

          public function serverWorksHere( server:Server ):Boolean

          {

              return servers.getItemIndex( server ) > -1 ? true : false'

          }

           

          I'm not sure of time complexity of the getItemIndex function, so I generally stick with a hashmap which comes in the form of a Dictionary.  The time complexity for hashmap operations is generally quoted on the order of O(1) -> O( log n ) depending on the hashing function used, either way, with the worst case being O(n) which we have already.  Using the dictionary means we have to modify the coffeshop class just a little bit.  Specifically using getters and setters for the arraycollection, and a little bookkeeping for the dictionary.  So our class now looks like this

           

          public class CoffeeShop (cause that's where I am right now )

          {

               //[Bindable] public var servers:ArrayCollection;

               [Bindable] public var customers:ArrayCollection;

               //Not really sure if this is how you declare an arraycollection inline, but you get the picture

               public var coffeeFlavors:Array= new Array("Sumatra" , "Kenyan" , "Colombian");

           

           

               [Bindable] public var serversOnDuty:ArrayCollection;

               [Bindable] public var address:String;

               [Bindable] public var name:String;

               public var shopFloorPlan:FloorPlan;

           

               //Our new dictionary friend

               private var _dictionary:Dictionary;

           

           

               private var _servers:ArrayCollection;

           

              [Bindable(Event="serversChange")]

              public function get servers():ArrayCollection

              {

                  return _servers;

              }

           

              public function set servers( value:ArrayCollection ):void

              {

                  _servers = value;

                 //use weak keys so objects aren't prevented from

                 // being gc'ed by the _dictionary

                 _dictionary = new Dictionary( true );

                 if( value )

                 {

                    for each( var server:Server in value )

                    {

                        //store the server id in the dictionary

                       _dictionary[ server.serverid ] = server;

                     }

                 }

              }

           

              public function addServer( server:Server ):Server

              {

                  _servers.addItem( server );

                  _dictionary[ server.serverid ] = server;

                  dispatchEvent( new Event("serversChange") );

                  return server;

              }

           

              public function removeServer( server:Server ):Server

              {

                  if( _servers.getItemIndex( server ) > - 1 )

                  {

                       //remove the server from the collection AND the server

                        _servers.removeItemAt( _servers.getItemIndex( server ) );

                       _dictionary[ server.serverid ] = server;

                        dispatchEvent( new Event("serversChange") );

                  }

           

                  return server;

              }

           

            //Pass only the id, helps if we later want to use any type of wrapper/adapter/interface later on

            public function serverWorksHere( serverId:String ):Boolean

            {

               return _dictionary[ serverId ] != null ? true : false;

             }

          }

           

           

          So here are a few optimizations that I use to make my life easier in terms of performance, cutting down on unnecessary databinding, changing my search functions and using custom getters and setters.  Another performance enhancer you may want to consider is the nesting of components, since some events bubble ( mouse click ), each node has to process that event.

           

          As far as the code you posted, there is a funny code smell with it.

           

          -Are the timers being used to time something, such as when a graphic should appear on the screen, or are these timers being used in reference to component instantiation.  Basically, was the timer created with thoughts like these "I should check now to see if the service has returned" ,  "I should check now to see if module/view class has loaded/completed/isavailable".  If so, I would begin to worry, seeing how there are many lifecycle events that can be used to let your app know when something has returned/is ready/is initialized.

           

          -The Label you posted is kind of scary.  A label is a view component, it shouldn't have any knowledge of your LanguageManager construct. That should be confined to a presenter or controller class and not be referenced by the view.  I too am a fan of morse code, but all the dots (forgot the real name) in the (LanguageManager.getManager().getText(LanguageManager.getManage r().texts, 'No data found') is a little excessive in my humble opinion.  In addition, its a singleton (LanguageManager), which can be the source of so much pain and suffering when being used incorrectly.  Ask yourself this, would it "smell better" if you were to have a controller/presenter for your view class, and inject the LanguageManger or whatever language data model into the presenter ?

          • 2. Re: Performance best practices
            FredericCox Level 1

            Thanks for helping me

             

             

            -Are the timers being used to time something, such as when a graphic should appear on the screen, or are these timers being used in reference to component instantiation.  Basically, was the timer created with thoughts like these "I should check now to see if the service has returned" ,  "I should check now to see if module/view class has loaded/completed/isavailable".  If so, I would begin to worry, seeing how there are many lifecycle events that can be used to let your app know when something has returned/is ready/is initialized.

             

             

             

            No that is not the case, I'm quite comfortable with the lifecycle methods but I do not use any other framework on top of Flex like Cairngorm or MVC. The timers are just running because on of our devs made an error instantiating them at the startup of the module so it is just running without any event listening or so... just wondering how performance intensive that is. I know we need to disable the creation of useless timers but it was more about finding out if that could be the big problem that slows down the application

             

             

             

            -The Label you posted is kind of scary.  A label is a view component, it shouldn't have any knowledge of your LanguageManager construct. That should be confined to a presenter or controller class and not be referenced by the view.  I too am a fan of morse code, but all the dots (forgot the real name) in the (LanguageManager.getManager().getText(LanguageManager.getManage r().texts, 'No data found') is a little excessive in my humble opinion.  In addition, its a singleton (LanguageManager), which can be the source of so much pain and suffering when being used incorrectly.  Ask yourself this, would it "smell better" if you were to have a controller/presenter for your view class, and inject the LanguageManger or whatever language data model into the presenter ?

             

             

            The LanguageManager loads the text values and therefore we call it from all modules since they will load in the shell application so LanguageManager will always be there. How would you solve such a problem where text for labels needs to run through a function to get the value of a XML object in the shell application, this seemed the easiest way to use it. Although I can optimize it to LanguageManager.getManager().getText('No data found') since we have disabled the bindable functionality on this one (so no on-the-fly translation possible anymore)

            • 3. Re: Performance best practices
              Flex harUI Adobe Employee

              Having a bunch of timers running will still take up time.  Use the profiler

              to see how much.