Launch Time Optimization

This topic presents a case study of improving an application to reduce its launch time. It describes the application launch process, techniques for reducing application loading time, and best practices for writing more efficient JavaScript.

Applications that offer a good user experience (UX) attract more users. Users who have positive impressions of an application return to it and are more likely to recommend it to others. If you monetize the application, this can result in greater profit.

Application launch time is the total time from the moment when the user clicks the application icon until the application is ready to use.
Improving the application launch time improves the user experience. Because application loading is the first experience the user gets of your application, a long loading time can leave them with a negative impression.
Therefore, it is worthwhile to prioritize reducing application launch time.

User satisfaction drastically decreases as the application launch time increases, as shown in the following figure based on a user survey. Using this information, a target launch time of around 5-7 seconds satisfies most users.

Figure 1. User satisfaction with application launch time

Application Launch Process

The application launch process can be divided into 5 stages. Some stages are controlled by the system, but most stages are dependent on application implementation, providing opportunities for performance improvement.

This case study is based on an application running on a 2015 Samsung Smart TV. Video was captured while launching the application on the TV, enabling the duration of each application launch stage to be measured with an accuracy of one video frame.

The following figure illustrates the application launch stages, and the time spent at each stage for the case study application.

Figure 2. Application launch timeline

The following table describes each stage in detail.

Stage Action Screen Time Description
1 Application icon click 1.086s Time between clicking the application icon to when the Smart Hub "Apps" panel disappears.
2 Initial loading screen 4.979s Time between loading the home page ("index.html") and calling the window.onload() method.

3 Black screen 0.837s Time a transition screen is shown before calling the window.onload() method (for Samsung Legacy Platform applications only).

4 Splash image 0.570s Time until the splash image appears. The splash image is the first screen shown in the application after calling the window.onload() method. It is usually shown during authentication or while content is loading.

5 Home page 0.348s Time taken to render the home page, fill it with content, and enable user input.

Table 1. Application launch stages

Optimizing Launch Logic

This section assumes that the case study application utilizes a framework. The framework contains modules responsible for activities, such as sending AJAX requests to the backend server, application authorization in the backend side, and modules responsible for the UI and logic.

The case study application loading process is illustrated in the following figure.

Figure 3. Original application launch logic

  1. The application logic is dependent on the framework setup. In stages 2 and 3, load and initialize the framework.
  2. After the framework setup is complete, start authorization at the backend server with the application logic.
  3. If authorization succeeds, send AJAX requests to the backend server to fetch application data, such as a list of recommended movies to display on the application home page.
    AJAX requests are dependent on the authorization step, because they must contain an authorization token.
  4. Wait for the data needed to render the home page, and render it.

This model is not optimized because the entire framework must be loaded and initialized before the application can start the authorization and content loading processes.

A possible approach to speeding up the launch process is by rebuilding the application in the following way.

Figure 4. Optimized application start logic

  1. Divide the framework setup into 2 phases:
    1. In the first phase, load the modules responsible for application authorization.
    2. In the second phase, initialize the rest of the framework.
  2. Once the first phase is loaded, perform authorization immediately. It is only dependent on the XHR object created in Stage 1 during Web runtime setup. Multiple AJAX requests are merged into 1 request to avoid unnecessary delays. The AJAX request is dependent on the authorization step for the authorization token.
  3. When the framework is fully initialized, begin performing the basic home page setup without waiting for the AJAX request response.
  4. Stage 3 is eliminated completely by migrating the application from Samsung Legacy Platform to Tizen.
  5. Padding time is needed to synchronize the application logic and AJAX requests. The application logic requires AJAX request data to render the home page, but as the requests are asynchronous, implement mechanisms to coordinate the modules, such as events, callbacks, and promises.
  6. Resume the application logic after receiving the response from the server.
  7. Load other parts of application logic, such as required scripts and data, on demand. In this way, they do not delay the launch process.

This flow is more optimized, as many tasks are performed in parallel. Initially, the application loads only the modules which are necessary for authorization and starts authorization immediately along with other operations, such as loading the rest of the framework, the UI, resources, and content. Any tasks not required for displaying the home page are postponed to start after the launch process. Consequently, application launch time is much shorter than before.

Improving Launch Time

The following table lists optimization strategies that can improve application launch time, and the application launch stages they affect.

Optimization Stage
1 2 3 4 5
Migrating application to Tizen + +
Enabling prelaunch + + + +
Minimizing home page code + +
Loading JS files asynchronously + +
Sending AJAX requests promptly + +
Parallelizing AJAX requests
Concatenating AJAX requests + + +
Delaying platform and Tizen API calls + +
Caching API output + + +
Using jQuery 2.x or higher + + +
Loading static resources locally + +
Minimizing and concatenating code + +
Removing obsolete code + +
Using sprites + + +
Using pure CSS + + +
Enforcing GPU acceleration +

Table 2. Launch stages affected by optimizations

Migrating Application to Tizen

  • Stages affected: 1 and 3

Tizen is more efficient than Samsung Legacy Platform. Migrating the application from Samsung Legacy Platform to Tizen can improve its performance significantly.

Enabling Prelaunch

  • Stages affected: 1, 2, 4, and 5

TV Web applications in Tizen support prelaunching. Applications can load in the background when the Samsung TV is switched on. When the user launches the application, it loads more quickly because it has been prelaunched.

For more information, see Prelaunching Applications.

Minimizing Home Page Code

  • Stages affected: 2 and 5

Application launch time can be improved by loading and parsing less code at launch. When the application launches, load only the HTML, JS, and CSS code necessary to show the home page. Other code can be loaded on demand using a library, such as require.js, to avoid building and parsing the DOM before it is needed.

Loading JavaScript Files Asynchronously

  • Stages affected: 2 and 4

When loading external JavaScript files normally, the HTML parser stops while the JavaScript files are downloaded and executed.

Figure 5. JavaScript loading from external files

To avoid the pause in HTML parsing during download, you can use the defer and async attributes:

<script src='js/main.js' defer></script>
<script src='js/scripts.js' async></script>	

Using the defer attribute downloads the JavaScript file in parallel while parsing HTML. The JavaScript code executes only when all HTML parsing is complete. Scripts using the defer attribute are guaranteed to execute in the same order that they appear in the document.

Figure 6. External JavaScript loading using the "defer" attribute

Using the async attribute downloads the JavaScript file in parallel while parsing HTML, but when the download completes, the HTML parser pauses to execute the script.

Figure 7. External JavaScript loading using the "async" attribute

The defer and async attributes can be used only for scripts implemented with the script element and src attribute.

For more information, see the HTML <script> element.

Handling AJAX Requests

You can improve launch time by optimizing AJAX request usage:

  • Sending AJAX requests promptly
    • Stages affected: 2 and 4
      Most applications load their content from remote servers and utilize a framework that helps manage the requests. However, loading and executing the framework code can take a lot of time. To speed up application launch, send the AJAX requests needed to display the home page as soon as possible.
      You can send a simple XHR request at the beginning of the "index.html" file, before loading the framework. XHR requests are asynchronous and do not block any other threads. Cache the response in a global object, so the application framework can use the data immediately to display the home page without sending the request again.
  • Parallelizing AJAX requests
    Send AJAX requests in parallel if they are not dependent on responses from prior requests:
    • Before:
      Each request is executed after receiving a response from the previous one:
      sendRequest1 
        onSuccess: sendRequest2 
          onSuccess: sendRequest3 
            onSuccess: sendRequest4 
              onSuccess: sendRequest5 
      
    • After:
      The first request authorizes the application. After receiving the response, the other requests are executed asynchronously:
      sendRequest1
        onSuccess: sendRequest2
                  sendRequest3
                  sendRequest4
                  sendRequest5
      
  • Concatenating AJAX requests
    • Stages affected: 2, 4, and 5
      Limit the number of AJAX requests by merging several requests, if possible. The fewer requests there are, the less time it takes to process all of them.

      For example, consider an application that sends 3 requests to display the home page, such as for application authorization, a list of recommended movies, and a list of the latest movies. Verify whether it is possible to modify the requests and backend in such a way that the response for the authorization request contains the recommended and latest movies as well.

      You can also use this strategy throughout the application to reduce the loading time of other scenes.

Delaying Platform and Tizen API Calls

  • Stages affected: 2 and 4

Many applications make early calls to Tizen and platform APIs for static information, such as the DUID or model code. Because API initialization is "lazy" (the API is initialized only when it is first needed), the first call to each API module takes time. Postpone API calls until the application is fully started and ready to use, if possible.

Caching API Output

  • Stages affected: 2, 4, and 5

Instead of querying the API each time, cache the static output from Product and Tizen APIs in JavaScript variables.

Consider the following scenarios:

  • The total duration of video content is constant during video playback. You can retrieve it once and store it in a JavaScript variable instead of querying the API each time it is needed.
  • TV specification parameters, such as the DUID or model code, never change. You can retrieve the parameters using the API the first time the application is launched and save it in localStorage. Retrieving data from localStorage is quicker than querying the API.

Using jQuery 2.x or Higher

  • Stages affected: 2, 4, and 5

Newer versions of jQuery are smaller and faster. You can also consider using a custom jQuery build.

Loading Static Resources Locally

  • Stages affected: 2 and 4

Load static application resources directly from the local file system, by including all application source code, external libraries, and static UI elements, such as CSS and images, within the Tizen package:

  • Before:
    <link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css'>
    <link rel='stylesheet' href='https://example.com/myapp/css/styles.min.css'>
    <script src='http://code.jquery.com/jquery-2.0.0.min.js'></script>
    <script src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js'></script>
    
  • After:
    <link rel='stylesheet' href='css/bootstrap.min.css'> 
    <link rel='stylesheet' href='css/styles.min.css'> 
    <script src='js/jquery-2.0.0.min.js'></script> 
    <script src='js/bootstrap.min.js'></script> 
    

This not only decreases loading time, but can also improve application security and eliminate some application errors caused by network issues.

Minimizing and Concatenating Code

  • Stages affected: 2 and 4

The application loads faster when it makes fewer requests to the file system. If you do not want to use a lazy loading mechanism to load parts of the application on demand, concatenate and minimize your source code, especially JavaScript and CSS files:

  • Before:
    <!DOCTYPE HTML>
    <html lang='en'>
    <head>
      <meta name='viewport' content='width=1280, user-scalable=no'>
      <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
      <title>My application</title>
      <link rel='stylesheet' type='text/css' href='styles/main.css'>
      <link rel='stylesheet' type='text/css' href='styles/list.css'>
      <link rel='stylesheet' type='text/css' href='styles/player.css'>
      <link rel='stylesheet' type='text/css' href='styles/login.css'>
      <link rel='stylesheet' type='text/css' href='styles/account.css'>
      <script src='js/main.js'></script>
      <script src='js/list.js'></script>
      <script src='js/player.js'></script>
      <script src='js/login.js'></script>
      <script src='js/account.js'></script>
    </head>
    <body>
      <div id='Menu'></div>
      <div id='mainContainer'>
      <div id='helpbar'></div>
    </body>
    </html>
    
  • After
    <!DOCTYPE HTML> 
    <html lang='en'> 
    <head> 
      <meta name='viewport' content='width=1280, user-scalable=no'> 
      <meta http-equiv='Content-Type' content='text/html; charset=utf-8'> 
      <title>My application</title> 
      <link rel='stylesheet' type='text/css' href='styles/all_styles.css'/> 
      <script src='js/all_scripts.js' defer></script> 
    </head> 
    <body> 
      <div id='Menu'></div> 
      <div id='mainContainer'> 
      <div id='helpbar'></div> 
    </body> 
    </html> 
    

In some situations, it can be better to concatenate your code into 2 files instead of 1:

  • If the code must be executed as soon as possible, put it into a file that is loaded with the async attribute.
  • If the code requires the DOM to be ready before execution, put it in another file that is loaded with the defer attribute.

Make sure that the code loads in the correct order to satisfy its dependencies.

Removing Obsolete Code

  • Stages affected: 2 and 4

If the application loads any unused source code, whether it is JavaScript, CSS, or HTML, the browser takes time to parse it, build the DOM, and search the HTML code for matching CSS rules. Keep your code clean and up to date to avoid unnecessary performance issues caused by obsolete or redundant code.

For example, when the window.onload() method is called, calling the unregisterKey() method of the TVInputDevice API is unnecessary because no keys are registered other than the default "Left", "Up", "Right", "Down", "Enter", and "Back" keys.

Using Sprites

  • Stages affected: 2, 4, and 5

If your application loads many small graphics, such as icons, button elements, and backgrounds, consider using CSS sprites. CSS sprites help images load faster by making fewer requests to the file system, which improves overall application performance.

You can merge all of the icons or background images into 1 large image and use CSS to set the positions for each element. Consider the following mouse hover implementation:

  • Before:
    Figure 8. Normal button
    Figure 9. Button hover effect
    #myButton {
      width: 300px;
      height: 100px;
      background: url('img/button-normal.png') 0px 0px no-repeat;
    }
    
    #myButton:hover {
      background: url('img/button-hover.png') 0px 0px no-repeat;
    }
    
    This code is not optimal, as the browser must make 2 requests to the file system instead of 1. Additionally, the first time the user hovers on the button, its background will flicker, as some time is needed to request the new image and show it on the screen.
  • After:
    Figure 10. CSS sprite
    #myButton { 
      width: 300px; 
      height: 100px; 
      background: url('img/button-sprite.png') 0px 0px no-repeat; 
    } 
    
    #myButton:hover { 
      background: url('img/button-sprite.png') 0px -100px no-repeat; 
    } 
    
    This code is more optimal because it loads only 1 image (which takes less time to load, even though its size is bigger). Mouse hover changes only the background image position, so there is no flickering when switching between the states.

Using Pure CSS

  • Stages affected: 2, 4, and 5

Tizen supports CSS3, which allows you to create many graphic effects without using image files. Pure CSS renders faster and takes less time to calculate its properties, which is especially valuable when animating CSS elements.

The following code creates a button using only pure CSS:

button {
  width: 180px;
  height: 62px;
  line-height: 62px;
  font-size: 24px;
  color: #fff;
  text-align: center;
  text-shadow: 1px 0 1px rgba(0, 0, 0, .75);
  background: -webkit-linear-gradient(
    top,
    #1e5799 0%,
    #2989d8 50%,
    #207cca 51%,
    #7db9e8 100%
  );
  border: solid 2px #fff;
  border-radius: 12px;
  box-shadow: 0px 0px 15px 5px rgba(255, 255, 190, .75);
}

Figure 11. Button rendered in CSS

Enforcing GPU Acceleration

  • Stage affected: 5

Normally, the browser applies GPU acceleration when there is some indication that a DOM element can benefit from it.

CSS3 allows you to force the browser to render elements with GPU acceleration. One of the simplest methods is setting a style indicating 3D transform, even when no real 3D transformation is applied:

.acceleratedElem {
  -webkit-transform: translateZ(0);
}

In general, 3D transformations in CSS force the browser to use the GPU.

The following declarations can reduce flickering during CSS transforms or animations:

.acceleratedElem {
  -webkit-backface-visibility: hidden;
  -webkit-perspective: 1000;
}

For more information, see An Introduction to CSS 3-D Transforms and Increase Site Performance With Hardware-Accelerated CSS.

Optimizing JavaScript Code

Follow a reliable and consistent coding standard and style for JavaScript, as it makes the code easier to write, easier to read, improves its maintainability, and makes it less error-prone.

The following section presents JavaScript best practices that are frequently used in the Web community.

Caching DOM Element References

Searching the DOM tree is a resource-hungry operation. If there are DOM elements on which you perform operations frequently, store them in JavaScript variables to avoid repetitive DOM traversal:

  • Prefer:

    var elem1 = document.getElementById('elem1');
    elem1.innerHTML = 'Lorem ipsum dolor';
    elem1.classList.remove('class1');
    elem1.classList.add('class2');
    
    var $elem2 = $('#elem2');
    $elem2.html('Lorem ipsum dolor sit amet');
    $elem2.addClass('class3');
    
  • Avoid:

    document.getElementById('elem1').innerHTML = 'Lorem ipsum dolor';
    document.getElementById('elem1').classList.remove('class1');
    document.getElementById('elem1').classList.add('class2');
    
    $('#elem2').html('Lorem ipsum dolor sit amet');
    $('#elem2').addClass('class3');
    

It can be helpful to store references to frequently-used DOM elements in a global object. For example:

var domElementsCache = {
  elDocument: window.document,
  elHtml: window.document.documentElement,
  elBody: window.document.body,
  elHead: window.document.head,
  elTitle: window.document.getElementById('title')
};

domElementsCache.elTitle.innerHtml('My title');

Avoiding Slow jQuery Selectors

jQuery selectors work slower than native ones. If application performance is critical, consider replacing jQuery selectors, for example:

  • ID selector:
    //jQuery
    var elem = $('#myElem');
    
    //native JS equivalent
    var elem = document.querySelector('#myElem');
    
  • Class selector:
    //jQuery
    var elems = $('.class1');
    
    //Native JS equivalent
    var elems = document.querySelectorAll('.class1');
    
  • TagName selector:
    //jQuery
    var elems = $('span');
    
    //Native JS equivalent
    var elems = document.querySelectorAll('span');
    
  • $.find():
    //jQuery
    var elems = $el.find('.target1, .target2, .target3');
    
    //Native JS equivalent
    var elems = el.querySelectorAll('.target1, .target2, .target3');
    

Using Native Methods

Native JavaScript methods are faster than wrappers from different libraries. In some cases, it can be helpful to use them instead of library methods:

var myArray = [1, 2, 3];

//Underscore
_.each(myArray, function(val) {
  console.log(val);
});

//jQuery
$.each(myArray, function(val) {
  console.log(val);
});

//Native JS equivalent
for (var i = 0; i < myArray.length; i++) {
  console.log(myArray[i]);
}

Removing Console Logging

Extensive logging decreases application performance, because each console.log() method call blocks the JavaScript thread in the browser. Other logging methods, such as the console.warn(), console.info(), console.error() methods, behave similarly. To improve application performance, remove log methods in the final build.

You can use a logger utility to easily switch logging on and off. Open source libraries, such as Woodman, are available, or you can write your own logger, such as the following example:

// Global object for storing application configuration 
var applicationConfig = {
  debugMode: true //enables logs in application
};

// Logger object factory
var Logger = function() {

  var logger = {};

  if (applicationConfig.debugMode === true) {
    logger = {
      log: function() {
        var args = Array.prototype.slice.call(arguments, 0);
        console.log(args);
      },

      info: function() {
        var args = Array.prototype.slice.call(arguments, 0);
        console.info(args);
      },

      warn: function() {
        var args = Array.prototype.slice.call(arguments, 0);
        console.warn(args);
      },

      error: function() {
        var args = Array.prototype.slice.call(arguments, 0);
        console.error(args);
      }
    };

  } else {
    logger = {
      log: function() {},
      info: function() {},
      warn: function() {},
      error: function() {}
    };
  }

  return logger;
};

// USAGE:
// For development and debugging
var applicationConfig = {
  debugMode: true
};

var myLog = new Logger(); // Initializes logger
myLog.log('test'); // Outputs 'test' to console

//For production
var applicationConfig = {
  debugMode: false
};

var myLog = new Logger(); // Initializes logger
myLog.log('test'); // Does nothing

Optimization Results

The principles described in this guide were applied to several Samsung TV applications, as listed in the following table.

Optimization Application
A B C D E F
Migrating application to Tizen + + + + + +
Enabling prelaunch + + + + + +
Minimizing home page code + + + + + +
Loading JS files asynchronously + + + + + +
Sending AJAX requests promptly + + + +
Parallelizing AJAX requests + + +
Concatenating AJAX requests + +
Delaying platform and Tizen API calls + + + + +
Caching API output + + + +
Using jQuery 2.x or higher + + + +
Loading static resources locally
Minimizing and concatenating code + + + + + +
Removing obsolete code + + +
Using sprites + + + + + +
Using pure CSS +
Enforcing GPU acceleration +
Caching DOM element references + + + + + +
Avoiding slow jQuery selectors + + +
Using native methods + +
Removing console logging + + + + +

Table 3. Optimizations applied to Samsung TV applications

The following table lists the launch time optimization results.

Application Before
Optimization

After
Optimization

Reduction Reduction (%)
A 7.234 s 4.422 s 2.812 s 38.87%
B 6.972 s 4.642 s 2.330 s 33.42%
C 4.160 s 3.140 s 1.020 s 24.52%
D 16.512 s 4.011 s 12.501 s 75.71%
E 4.090 s 3.153 s 0.937 s 22.91%
F 17.853 s 7.462 s 10.391 s 58.20%
Average Reduction:

4.999 s

42.27%

Table 4. Results of application launch time optimization

The average reduction in application launch time was almost 5 seconds, a 42% improvement. This result suggests that it is worthwhile to optimize applications, wherever possible.