top

Performance Guide for Web App

Published 2014-10-28 |

This document describes how to optimize running and operational performance when developing Web App on Samsung Smart TV. This guide satisfies both general characteristics of JavaScript and functional characteristics of Samsung Smart TV Run Time so that you could expect excellent operating performance, if you follow it.

Strategy of Quick Starting App

Summary

Samsung SMART TV’s Web App has the following execution sequence.

Figure 1: Sequence of executing Apps

In [Figure 1], the time it takes to run the app is marked Total elapsed time it is equal to the total time which is spent in Platform and App.

After the user has selected App, the time with the exception of approximately 800 ms at the platform, you can reduce Time A and Time B by App’s performance improvement. If this time is improved to around 3 seconds, App will run within 5 seconds. At Samsung SMART TV, it is acknowledged that App has enough fulfillment performance if App is performed within 5 seconds.

Minimize initial loading

Minimize index.html

In order to implement the app run faster, as shown in [Figure 1], the reduction of execution time which loading "index.html" and executing "onload" is needed. To do this, it is important to minimize initial loading files. Strategy which necessary files are loaded after the initial loading is needed.

Figure 2: Minimize initial loading

This figure is "index.html" contents which is to be included in the App, JS files which are loaded at App initial loading are marked with a red square. It contains only the necessary contents, so App run-time to be able to give sufficient performance. Containing less than five is a recommendation and if you reduce the number of files than the page loading time is reduced. Above all, TV device’s flash memory access frequency is reduced so operation speed is more quickly.

  • Load only the files needed for the initial screen and then loading the necessary JS files.
  • When importing IME and SSO related modules, you implement it at the actual timing of the use.
  • Libraries such as jQuery are loading if necessary.

Display first screen faster

Because most of the apps has the first screen, it is important that first screen pops up as soon as possible. The goal of Minimize "index.html" is also here. It is better to load JS files and CSS files for showing first screen as quickly and then start other tasks. However, the result of the communication server is configured as a screen there may be difficulties. So caching the last data and using it temporarily in the first screen configuration.

  • Displaying first screen, and later working other tasks.
  • Caching the last data from server, after the first screen configuration communicate with the server.

Insert image into <body>

  • Insert background image with 'div' is changed to insert image into <body>.
  • In the existing body, insert background image by using <img> and src tag, but this case performance issues can occur because of duplication of images.

Example

<body style="cursor: default; background-image: url(images/1080p/sub_bg_01.jpg);">

JS Optimizing / Minifying

Using minify (YUI optimizing tool, Google Closure) tool.

window.location.search practical use

TV Device’s status values are received from "window.location.search" to maximize parameters when running the App. Using API is a time consuming job, so do not use API when the initial loading for better performance.

  • window.location.search

    Parameter Detail
    country Serviced country code (e.g. KR, US, GB, IT, AU etc.)
    lang Setting language code (e.g. en, ko, fr etc.)
    modelid TV Device model code (e.g. 13_FOXP, 13_X14 etc.)
    server Connection server Type (operating : 0, development : 1)
    area TV release destination (e.g USA, KOR, PANEURO etc.)
    product Product Type (TV: 0, Monitor: 1, BD: 2)
    mgrver Widget Manager version
    id CP account ID connection to SSO
    pw CP account Password connection to SSO

Use Remote Web Inspector to measure execution time

You can measure real App operating time by using Remote Web Inspector. This guide will explain basic of how to measure operating time, please refer to this link if you want more information and other details.

Connecting to Remote Web Inspector

In order to connect Remote Web Inspector, TV Device which is connected to SDK is needed. Refer to the linked page to connect TV and SDK.

Measuring operating speed at specific point of time

Step 1

Connect to the Remote Web Inspector first page. Address is the IP address which is given to the TV you can connect with 7011 port. As an example, choose accuWeather App.

Step 2

When you selected App you can see elements of the App. Now select "Network" tab and measure operating performance.

Step 3

The performance measurement section was not set up, you can’t see anything. In order to make the measurements at this point, press “Record” ● button at the bottom of the screen. Button will be changed to red and measurement is started.

Step 4

If an App is performed, performed JS or image decoding time will be displayed. If you move mouse cursor over each element, you can see detail information.

Optimize communication with server

Authentication token caching

If you caching the token which is needed when the App connects to CP (Contents Provider) server, you can reduce server authentication time which generated during the reconnect.

Keep alive

If image or data are changed continuously on the screen, you can reduce response time by maintaining connection with the server for a certain period of time.

Parallelization

When multiple server communication is required, if sequence communication is unnecessary, request to the server at the same time as the communication time must be minimized.

Optimize loading image

Image Lazy Loading

If image tag exists in html markup, Webkit try downloading the images in the background even if it is not seen in a screen. At this time, unnecessary resource can have an adverse effect on performance. You can apply easily by using the Lazy Load Plugin for jQuery library.

Example

<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="jquery.lazyload.js"></script>

<script>
    function onLoad() {
        $('img.lazy').lazyload();
    }
</script>

<body>
    <img class="lazy" data-original="img/example.jpg" width="765" height="574">
</body>

Reference: http://www.appelsiini.net/projects/lazyload

CSS Sprite

CSS sprites are a way to reduce the number of HTTP requests made for image resources referenced by your site. Images are combined into one larger image at defined X and Y coordinates. Having assigned this generated image to relevant page elements the background-position CSS property can then be used to shift the visible area to the required component image. This technique can be very effective for improving site performance, particularly in situations where many small images, such as menu icons, are used. By using CSS Sprite Generator, CSS Sprite will be created simply.

Example

#content {
   background: url(/images/your_image_path.png) no-repeat 0 0;
   background-position: 100px 100px;
   width: 100px;
   height: 100px;
}

Reference: http://spritegen.website-performance.org/

JavaScript Basic tips

Define local variables

When a variable is referenced, JavaScript hunts it down by looping through the different members of the scope chain. This scope chain is the set of variables available within the current scope, and across all major browsers it has at least two items: a set of local variables and a set of global variables. Simply put, the deeper the engine has to dig into this scope chain, the longer the operation will take. It first goes through the local variables starting with this, the function arguments, then any locally defined variables, and afterwards iterates through any global variables. Since local variables are first in this chain, they’re always faster than globals. So anytime you use a global variable more than once you should redefine it locally, for instance change:

Example

Before

var a = document.getElementById("aaa");
var b = document.getElementById("bbb");

After

var doc = document; // document is defined to local varialbes
var a = doc.getElementById("aaa");
var b = doc.getElementById("bbb");

Don’t use the with() statement

It’s pretty much a fact: the with() statement is pure JavaScript evil. This is because with() appends an extra set of variables to the beginning of the scope chain described above. This extra item means that anytime any variable is called, the JavaScript engine must loop through the with() variables, then the local variables, then the global variables. So with() essentially gives local variables all the performance drawbacks of global ones, and in turn derails JavaScript optimization.

Example

Before

var usr = { name: "name", langunage: "kr"};

with(usr) {
    console.log(name);
}

After

var usr = { name: "name", langunage: "kr"};

console.log(usr.name);

Use closures sparingly

Closures are a very powerful and useful aspect of JavaScript, but they come with their own performance drawbacks. Although you may not know the term closures, you probably use them often. Closures can basically be thought of as the new in JavaScript, and we use one whenever we define a function on the fly, for instance:

Example

Before

for (var i = 0; i < 10; i++) {
    var fn = function() {
        console.log(i);
    };
    fn();
}

After

function log(n) {
    console.log(n);
}

for (var i = 0; i < 10; i++) {
    log(i);
}

Avoid for-in loops (and function based iteration)

The logic behind this is pretty straightforward: instead of looping through a set of indexes like you would with a for or a do-while, a for-in not only might loop through additional array items, but also requires more effort. In order to loop through these items, JavaScript has to set up a function for each one. This function-based iteration comes with a slew of performance issues: an extra function is introduced with a corresponding execution context that is created and destroyed, on top of which an additional object is added to the scope chain.

Example

Before

var arr = [1, 2, 3, 4];
for (var i in arr) {
    console.log(arr[i]);
}

After

var arr = [1, 2, 3, 4];
var length = arr.length;
for (var i = 0; i < length; i++) {
    console.log(arr[i]);
}

Define arrays for HTML collection objects

JavaScript uses a number of HTML collection objects such as document.forms, document.images, etc. Additionally these are called by methods such as getElementsByTagName and getElementsByClassName. If it is used in loop, it will be horrible effect.

Example

Before

for (var i = 0; i < document.forms.length; i++) {
    var f = document.forms[i];
    // ...
}

After

var forms = document.getElementsByTagName("form");
for (var i = 0; i < forms.length; i++) {
    var f = forms[i];
    // ...
}

Stop touching the DOM, damn it!

Leaving the DOM alone is another big topic in JavaScript optimization. The classic example is appending an array of list items: if you append each of these to the DOM individually, it is considerably slower than appending them all at once. This is because DOM operations are expensive. DOM operations are resource-heavy because of reflow. Reflow is basically the process by which the browser re-renders the DOM elements on the screen. For instance, if you change the width of a div with JavaScript, the browser has to refresh the rendered page to account for this change. documentFragment is basically a document-like fragment that isn’t visually represented by the browser. Having no visual representation provides a number of advantages; mainly you can append nodes to a documentFragment without incurring any browser reflow.

Example

Before

var trash = document.getElementById("trash");
// ... Modify trash
document.getElementById("target").appendChild(trash);

After

var trash = document.getElementById("trash");
var frag = document.createDocumentFragment();
frag.appendChild(trash);
// ... Modify trash
document.getElementById("target").appendChild(frag.cloneNode(true));

Change CSS classes not styles

You may have heard that changing CSS classes is more optimal than changing styles. This boils down to another reflow issue: whenever a layout style is changed, reflow occurs. Layout styles mean anything that might affect the layout and force the browser to reflow. For instance, width, height, font-size, float, etc. Instead of incurring a reflow penalty every time you change a given style, you can use a CSS class to change a number of styles at once, and in turn only incur a single reflow.

Example

Before

var elm = document.getElementById("elm");
elm.style.backgroundColor = "#fff";
elm.style.padding.left = "0px";

After - CSS

.elm {
    background-color: #fff;
    padding-left: 0px;
}

After - HTML

<div class="elm">...</div>

Release object

If release the used object immediately, do not stack in GC, it is any good effect for performance.

Example

var a = new Array(100);
delete a; // Release object

Don’t use element.src="" for initializing

If image element.src don’t have any value, it will be occurred internal error. In this case, GC will not work.

Optimization of UI

Summary

Recently Web Apps have Rich UI, so it too many resource for using animation elements and performance issues. This chapter include example and guidance for resolving Web Apps performance.

Minimize Repaint and Reflow

Repaint (or Redraw)
Repaint is what happens whenever something is made visible when it was not previously visible, or vice versa, without altering the layout of the document. An example would be when adding an outline to an element, changing the background color, or changing the visibility style. Repaint is expensive in terms of performance, as it requires the engine to search through all elements to determine what is visible, and what should be displayed.
Reflow

A reflow is a more significant change. This will happen whenever the DOM tree is manipulated, whenever a style is changed that affects the layout, whenever the className property of an element is changed, or whenever the browser window size is changed. The engine must then reflow the relevant element to work out where the various parts of it should now be displayed. Its children will also be reflowed to take the new layout of their parent into account. Elements that appear after the element in the DOM will also be reflowed to calculate their new layout, as they may have been moved by the initial reflows. Ancestor elements will also reflow, to account for the changes in size of their children. Finally, everything is repainted.

Reflows are very expensive in terms of performance, and is one of the main causes of slow DOM scripts, especially on devices with low processing power, such as phones. In many cases, they are equivalent to laying out the entire page again.

How to minimize Reflow?

In case of Change of style with class, change the node which exist the end of DOM structure as much as possible.
Even though you can’t avoid Reflow from class change, you can reduce effect. If you change node which located as possible as the end of DOM tree, area of reflow may be restricted to some of the nodes, not the entire page. So you have to avoid class modifying in wrapper which cover the whole page. Also class is changed in OOCSS(Object Oriented CSS) ways by minimizing the effect of the reflow the performance gets better.
Elimination inline style as possible.
DOM is very slow structure. Also given the style on inline, reflow page will generate several times over the whole page. If there is no inline style, reflow will be generated only once by the combination of an external style class.
Configure animation element by "position: fixed or position: absolute" if possible.
Generally the animation for width/height or position movement on JavaScript (especially jQuery) or CSS3 induce considerable reflow almost second by second. If the element’s position property is set up "fixed" or "absolute" in this case, it couldn’t have an effect to the layout of other elements. It’s very efficient way from cost.
Make compromise between qualities and performances.
If there are two animations A and B, A is moving 1px by one time and B is moving 3px by one time, CPU performance costs will generate because animation calculation and page reflow calculation are generated simultaneously. At this time, A costs more than B. At a fast device both similar but the difference is noticeable in the slow device.
Avoid using table layout.
Progressive page rendering is not applied to page layout composed table, and is able to display after load and calculate all of page. Moreover, according to Mozilla, it can bring about Reflow on the entire nodes in the table even a little change in table layout. Also according to Jenny Donnelly, Developer of YUI data table Widget, even if the table’s use is for not layout but data expression, setting up the table’s property to "fixed" (table-layout: fixed) on the table is better than "auto", the default property, in performance.
Handle at a time as possible, if you need to change element’s style with Javascript.

When you need to change a specific element’s style, you can try like below.

Example

var toChange = document.getElementById('elem');
toChange.style.background = '#333';
toChange.style.color = '#fff';
toChange.style.border = '1px solid #ccc';

These approaches causes overlapping reflow and repaint. So, it’s more effective than the above that just handle at a time.

Example - CSS

#elem { border:1px solid #000; color:#000; background:#ddd; }
.highlight { border-color:#00f; color:#fff; background:#333; }

Example - JavaScript

document.getElementById('elem').className = 'highlight';
Rearrange low selectors of CSS as you need.

It’s the content for CSS Recalculation which Reflow causes, not only for Reflow itself. Rule matching process of CSS is to flow as right core selector to left side. It keeps the process going until facing unmatched or mismatched to the rules. If you used it as you really need with the specially of its CSS, the performance is getting improved. (In short, cost effective as the rule is less.) There are two examples when btn_more class of list_service is unique.

Example 1

.section_service .list_service li .box_name .btn_more {
    display: block;
    width: 100px;
    height: 30px;
}

Example 2

.section_service .list_service .btn_more {
    display: block;
    width: 100px;
    height: 30px;
}

Even though the specialty of btn_more is invalid CSS, it’s the reason for code readability when used it like Example 1. Form the point of maintenance view, the readability is important but the performance will be lowered when the rules are 5 levels like Example 1. Moreover, when the length of these CSS codes is 500 to 1000 lines, not 5 to 10 lines, it can be a dominant effect. Because of this, it needs core CSS Rule definition like Example 2 and CSS annotation about its part is effective for code readability. It is better, the number of low selector are less.

Using GPU Accelerator

Minimize reflow

If change top, left, width, height directly, it will be occurred reflow. So use transform.

Example

Before

@-webkit-keyframes "move" {
     from { left: 0px; }
     to { left: 200px; }
}

After

@-webkit-keyframes "move" {
     from { -webkit-transform: translateX(0px); }
     to { -webkit-transform: translateX(200px); }
}

Using requestAnimationFrame for effective animation

If change top, left, width, height directly, it will be occurred reflow. So use transform.

 

Before : Using setTimeout, setInterval
Calling rendering unnecessarily because of setTimeout or setInterval which can’t be real Rendering in Web Engine makes CPU overload increase and waste electrical power. In addition, the side effect, which Animation waves, often happens because Rendering of Web Engine is not synchronized. It’s the issue for timing and it registers defect for impossible reappearance or undefinable matters.
After: Using requestAnimationFrame

requestAnimationFrame let Rendering performance of CPU and Web Engine increase because it let user code know when it needs rendering exactly, so it prevents unnecessarily rendering calling.

requestAnimationFrame is the defined API in W3C.

Reference : http://dvcs.w3.org/hg/webperf/raw-file/tip/specs/RequestAnimationFrame/Overview.html

Example

var requestId = 0;
var animationStartTime = 0;

function animate(time) {
    var frog = document.getElementById("animated");
    frog.style.left = (50 + (time - animationStartTime)/10 % 300) + "px";
    frog.style.top = (185 - 10 * ((time - animationStartTime)/100 % 10) + ((time - animationStartTime)/100 % 10) * ((time - animationStartTime)/100 % 10) ) + "px";
    var t = (time - animationStartTime)/10 % 100;
    frog.style.backgroundPosition = - Math.floor(t / (100/2)) * 60 + "px";
    requestId = window.requestAnimationFrame(animate);
}

function start() {
    animationStartTime = window.performance.now();
    requestId = window.requestAnimationFrame(animate);
}

function stop() {
    if (requestId) {
        window.cancelAnimationFrame(requestId);
    }
    requestId = 0;
}

Reference: http://dev.opera.com/articles/view/better-performance-with-requestanimationframe/

Using CSS3

How to minimize Reflow?
Before

Calling the function for changing left value regularly using timer when used the method which changes a lot of loading images to one image in a row for showing specific area.

Before

/* The method that changes CSS left value. It changes a lot of loading images to one image in a row for showing specific area.*/

var IMAGE_PX = 132;
var nSeq = 0;

//...

var timer = setInterval("animateImage()",80); /* Change left value using setInterval function */

function animateImage(){
    var nLeftValue = -(IMAGE_PX * nSeq);
    $("#loadingImg").css('left', nLeftValue);
}
After

Rotation one image, using keyframes.

Improvements

  • Loading animation is performed more smoothly.
  • Easy control of the animation (show, hide, speed changes, etc.)
  • Resolving infinite loading problem caused by duplicate timer

After - CSS

@-webkit-keyframes LoadingCtrlAni {
     0% { -webkit-transform: rotate(0deg); } /* Start animation */
     100% { -webkit-transform: rotate(360deg); } /* End animation */
};

#div {
    -webkit-animation-name: LoadingCtrlAni, /* Name of animiation keyframes */
    -webkit-animation-duration: 1440ms, /* Durations */
    -webkit-animation-timing-function: linear, /* Same with linear, ease*/
    -webkit-animation-iteration-count: infinite, /* None stop animation infinity */
    -webkit-animation-play-state: paused, /* Attribute value of stopping or running animation */
}

After - JavaScript

$("#loadingImg").css({'-webkit-animation-play-state': 'running'}); // Show() operation
$("#loadingImg").css({'-webkit-animation-play-state': 'paused'});  //Hide() operation

Cautions & Restrictions

In this section, it is summarized in the Web Engine’s functional restrictions and cautions by based on many problems that is occurred meanwhile. Using the guidelines, the aim is to know the present state of Samsung Smart TV, and to minimize trial and error when developing the App.

Cautions

Use Alias error

In the case that use “$/TEMP” instead of “$TEMP” in App, it works abnormally.

Wrong Example

<body style="background: #fff; margin: 0;" onkeydown="handleKeydown();">
   <img src="/dtv/temp/test.jpg" style="position: absolute; left: 0px; top: 270px; width: 960px; height: 540px;"/>
   <img src="$/TEMP/test.jpg" style="position: absolute; left: 960px; top: 270px; width: 960px; height: 540px;"/>
</body>

Undefined Callback function

When this function is not implemented, JavaScript error is occurred and it works abnormally.

Wrong Example

Player.init = function () {
    //...
    //Buffering
    this.plugin.OnBufferingStart = 'Player.onBufferingStart';
    this.plugin.OnBufferingProgress = 'Player.onBufferingProgress'; //Error is occurred because of using unimplemented callback function
    this.plugin.OnBufferingComplete = 'Player.onBufferingComplete';
    //...
}

./js/player/Player.js

//Player.onBufferingProgress = function (percent)
//{
//}

Avoid continuous use of appendChild()

Continuous use of appendChild() can make to increase use of memories, and it can make stop the App by limited memories in TV System. In the case that periodic use of "appendChild()", if this page is refreshed, you can delete allocated memories.

Wrong Example

<html>
<head>
<script type="text/javascript">
    var baseImgUrlOdd = 'Desert.jpg';
    var baseImgUrlEven = 'Jellyfish.jpg';
    var counter = 0;
    var intervalId = -1;

    function onLoad() {
        var container = document.getElementById('imageContainer');
        intervalId = setInterval(function () {
            counter++;
            var baseUrl = counter % 2 == 0 ? baseImgUrlEven : baseImgUrlOdd;
            var imgUrl = baseUrl + '?' + counter;
            var b = container.removeChild(container.firstChild);
            var a = document.createElement("img");
            a.src = imgUrl;
            container.appendChild(a);
        }, 1000);
    };
</script>
</head>
<body onload="onLoad();">
    <div id="imageContainer"></div>
</body>
</html>

Restrictions

Restrictions of 2012 products

AC Overflow

Escaping the scope of parent’s renderer through transform properties is called 'overflow'. When overflow property is defined in CSS properties of parent, it process overflowed child renderer properly (ex. overflow: hidden -> overflowed display is hidden). If the overflowed child renderer is off each other because of the conditions of a render compositor (ex. transform attribute – rotate, scale, skew, ...). It can make the problem that it calculate (position calculate) overflow property of parent renderer incorrectly in 2012 WebKit codes because of lower version.

Example of malfunction

animClass {
    -webkit-perspective: 1000px;
    perspective: 1000px;
    -webkit-transform: translateZ(0);
    transform: translateZ(0);
}

.ticker {
    position: absolute;
    overflow: hidden;
    border: 1px solid transparent;
}

Restrictions of 2013 products

2D Canvas acceleration

The problem can occur, when using Canvas in the TV App. In this case, if you don’t use the acceleration function under H/W restriction, it works normally. You can unused the function, <canvasGL>n<canvasGL> in config.xml.

Example of malfunction

function canvas_img(img) { // img : image file path
    oCanvas.domReady(function() {
        var canvas = document.getElementById("canvas");
        mycanvas = oCanvas.create({
            canvas: canvas,
            background: "#fff"
        });
        //operate.restore();
        var image = mycanvas.display.image({
        //... omit ...
        });
        mycanvas.addChild(image);
        mycanvas.draw.redraw();
        setTimeout(function() {
            document.getElementById('toDataURL_text').innerHTML =
                document.getElementById("canvas").toDataURL("image/png"); // Display canvas data
        }, 3000);
    });
}
Anti Aliasing

The operating is rough when working CSS Transform, It’s occurred (jagged edge effect) because didn’t work the process of anti-aliasing at this point of applying to rotation.

Example of malfunction

#loading {
    position: absolute;
    left: 100px;
    top: 100px;
    width: 90px;
    height: 90px;

    background-image: url(loading_90x90.png);

    -webkit-animation: loading 1440ms linear infinite;
}

@-webkit-keyframes loading {
    from { -webkit-transform: rotate(0deg) }
    to { -webkit-transform: rotate(360deg) }
}

Restrictions of both 2012 and 2013 products

Do not support Web Font

Example of malfunction

font-family: 'roboto-condensed-regular', arial, sans-serif;

Reference