Saturday, April 17, 2010

JavaScript setTimeout performance measurement

Intro

In HTML browser applications timer events are used all over. And it could impact usability of HTML application in various ways. From slowness of UI interaction to memory leaks and eventually death of application.

The application profiling methods are used for code efficiency measurement. Profilers are available in most popular browsers to some extent, but the  high level analysis stays in developer's hands and quite subjective. Especially when it comes to comparison of alternative code implementations.

Scope

  • expose available measurements of performance and HTML/JS engine load
  • enumerate some methods of efficiency improvement
  • compare the methods in numbers

Measurements

The timer-related activities in HTML application mostly exposed as timer event handling. Most popular low-level API is setTimer, setInterval, postMessage and special use of XHR. Platform also could provide own specific methods.

Difficult part is how to count application performance impact on those timer event handlers changes in attempt to tune them up.

I see the few criterias could be measured:

  1. Event handler timing. It will be most efficient and adequate method if not facing few crucial "buts"
    • discrete step for timer by JS new Date().getTime() stays is in the range of 15-30 ms. Which suppose to be longer of length for most timer event handlers.
      But it will work for long (longer of  double detectable time) timing events.
    • due to lack of reasonable functionality in native timer API event handlers are usually wrapped by framework API. Which on its own has big impact on short event handlers. For those in addition to individual handler timing, the framework wrapper need to be tracked as well.
  2. System CPU load. Obviously it is indirect and not precise. Will work only in conditions where CPU utilization is high enough and impact of say removal of timer event handler will be noticeable. The artificial bursting of timer event handler calls to the level of good detection is needed. Also there is no CPU utilization JS API exist. It could be simulated by count per second of dummy events. postMessage or setTimeout w/ zero interval. Just need to be sure that platform does that asynchronously.
    Another option is to have constant timeout but incrementally increase the load. Say just loop on heavy math computations. Operations counter per second until the timer interval is reached will give the CPU availability.

    CPU load timing is not intrusive and do not chance characteristics of recearched event handler. Also it allows check system load overall. I.e. no special treatment for specific timer event handler. Also could be used for system performance tracking on other recurrent event handlers like drag, mouse movement, progress animations, etc.

Tips

My todo list during timer handlers optimization.

  • Check the timing for event handler. If the execution time for event handler is sizeable (>30ms) than we have easiest case and all needed is the timing stats. Better to have average computation capabilities and good sampling set. Average calculation could be oursourced instead of embedded in JS. Obvoiusly accounting in proper/no exceptions execution: try/finally impacting the performace themselves
    • Collection of timing stats.  console.time  console.timeEnd will be sufficient
      If those are not available (like in IE)
    • get time @ start
    • print delta time @ end in format available for further average processing.
    • OR keep global counter and total execution time, printing out average every time @ end
  • If event handler execution time is short or you do not want to alter the event handlers themselves, than CPU load will be the criteria.
    • Create the "CPU filler" routine. It shall act as low-priority thread, letting remaining app to run.
    • setTimeout with counter increment and  comparing of the current time with last detected. Once new time value changed, update the stats: counter per second overall and for last second.
    • Other statistics could be handy. Like max time delay between filler calls - matching longest routine. Since there are few routines need to be tracked separately, routine could set it's key and reset last timer value. That key will be used for stats collection.
    • Special treatement need to be done for 0 interval: it shall be ignored in MIN stat computation due to minimum detectable interval (~30 ms)
  • Trigger on/off collection of stats. Reset stats. The timing functionality could be expensive. Especially in CPU load timing. The application load for development comfort need to be as fast as possible.
    Also the load timing is a separate problem and shall not be mixed with timer events profiling. From another side it has a reason to trigger profiling programmatically to see the impact in exact conditions and avoid mix with inrelated statistics. Like start on begin drag and stop on release. 
    • Have a global flag(s) or hash map of "profiling enabled" flags. Check the flag before collecting/printing timing
    • Have the triggering code on start/stop or after application load. In Web 2.0 application the onLoad for body is not a proper place to start time-based functionality (and obviously the timer events profiling): it is still the heavy initialization phase.
  • Finally on app level
    • collect stats for current app.
    • validate by simulation of ideal performance tuning: timer event handler have only return in body.
    • do the real stuff and see how it goes!

 

Links

©2010 Sasha Firsov

No comments:

Post a Comment