Improving performance in Hilo (Windows Store apps using JavaScript and HTML)

[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]

From: Developing an end-to-end Windows Store app using JavaScript: Hilo

Previous page | Next page

The Hilo team spent time learning what works and what doesn't work to create a fast and fluid Windows Store app. Here are some tips and coding guidelines for creating a well-performing, responsive app.

Download

After you download the code, see Getting started with Hilo for instructions.

You will learn

  • Tips that help create a fast and fluid app.
  • The differences between performance and perceived performance.
  • Recommended strategies for profiling an app.

Applies to

  • Windows Runtime for Windows 8
  • Windows Library for JavaScript
  • JavaScript

Performance tips

A fast and fluid app responds to user actions quickly, and with matching energy. The Hilo team spent time learning what works and what doesn't work to create a fast and fluid app. Here are some things to remember:

  • Limit the start time
  • Emphasize responsiveness
  • Use thumbnails for quick rendering
  • Retrieve thumbnails when accessing items
  • Release media and stream resources when they're no longer needed
  • Optimize ListView performance
  • Keep DOM interactions to a minimum
  • Optimize property access
  • Use independent animations
  • Manage layout efficiently
  • Store state efficiently
  • Keep your app’s memory usage low when it's suspended
  • Minimize the amount of resources that your app uses

Limit the start time

It's important to limit how much time the user spends waiting while your app starts. You can dramatically improve the loading time of an app by packaging its contents locally. In turn, this leads to other performance benefits, such as bytecode caching. Bytecode caching is a technique in which the system creates bytecode for each JavaScript file once, rather than re-creating the bytecode each time it starts the app. This technique improves load time by approximately 30 percent in a larger app. To benefit from bytecode caching, you must ensure that all JavaScript files are UTF8 encoded with a byte-order mark, and ensure that all JavaScript files are statically referenced in the root of your HTML start page. For more info, see Reducing your app’s loading time.

[Top]

Emphasize responsiveness

Don't block your app with synchronous APIs, because if you do the app can't respond to new events while the API is executing. Instead, use asynchronous APIs that execute in the background and inform the app when they've completed by raising an event. You should also break intensive processing operations into a series of smaller operations, allowing the app to respond to user input in between these operations. For more info, see Executing code.

[Top]

Use thumbnails for quick rendering

The file system and media files are an important part of most apps, and also one of the most common sources of performance issues. File access is traditionally a key performance bottleneck for apps that display gallery views of files, such as photo albums. Accessing an image can be slow because it takes memory and CPU cycles to store, decode, and display the image.

Instead of scaling a full-size image to display as a thumbnail, use the Windows Runtime thumbnail APIs. The Windows Runtime provides a set of APIs backed by an efficient cache that enables an app to quickly get a smaller version of an image to use for a thumbnail. These APIs can improve code execution times by a few seconds and improve the visual quality of the thumbnail. And because these APIs cache the thumbnails, they can speed up subsequent launches of your app. For more info, see Accessing the file system efficiently.

[Top]

Retrieve thumbnails when accessing items

In addition to providing an API for retrieving thumbnails, the Windows Runtime also includes a setThumbnailPrefetch method in its API. This method specifies the type and size of thumbnails that the system should start loading immediately when items are accessed, instead of retrieving them on a case-by-case basis.

In Hilo JavaScript, the MonthPresenter class queries the file system for photos to display on the month page that meet a specific date criteria, and returns any photos that meet that criteria. The _getImageQueryOptions function uses the setThumbnailPrefetch method to return thumbnails for the files in the query result set. Here's the code:

Hilo\Hilo\month\monthPresenter.js

_getImageQueryOptions: function () {
    var queryOptions = new search.QueryOptions(search.CommonFileQuery.orderByDate, [".jpg", ".jpeg", ".tiff", ".png", ".bmp", ".gif"]);
    queryOptions.setPropertyPrefetch(fileProperties.PropertyPrefetchOptions.none, [itemDateProperty]);
    queryOptions.setThumbnailPrefetch(fileProperties.ThumbnailMode.picturesView, 190, fileProperties.ThumbnailOptions.useCurrentScale);
    queryOptions.indexerOption = search.IndexerOption.useIndexerWhenAvailable;
    return queryOptions;
},

In this case, the code retrieves thumbnails that display a preview of each photo, at a requested size of 190 pixels for the longest edge of the thumbnail, and increases the requested thumbnail size based on the pixels per inch (PPI) of the display. Using the setThumbnailPrefetch method can result in improvements of 70 percent in the time it takes to show a view of photos from the user's Pictures.

[Top]

Release media and stream resources when they're no longer needed

Media file resources can greatly increase the size of your app's memory footprint. So it's important to release the handle to media as soon as the app is finished using it. Releasing media streams that are unused can significantly reduce the memory footprint of your app and help keep it from being closed when it's suspended.

For example, Hilo releases instances of the InMemoryRandomAccessStream class when it no longer needs them by calling the close method on the objects. Here's the code:

Hilo\Hilo\imageWriter.js

if (memStream) { memStream.close(); }
if (sourceStream) { sourceStream.close(); }
if (destStream) { destStream.close(); }

For more info, see Accessing the file system efficiently.

[Top]

Optimize ListView performance

When you use a ListView control, there are a number of ways to optimize performance. You should optimize the following experiences:

  • Initialization. The time interval starting when the control is created and ending when items are shown on screen.
  • Touch panning. The ability to pan the control by using touch and ensure that the UI doesn't lag behind the touch gestures.
  • Scrolling. The ability to use a mouse to scroll through the list and ensure that the UI doesn't lag behind the mouse movement.
  • Interaction for selecting, adding and deleting items.

The ListView control depends on the app to supply the data sources and templating functionality to customize the control for the app. The way these are configured in the ListView implementation has a large impact on its overall performance. For more info, see Using ListView and Working with data sources.

[Top]

Keep DOM interactions to a minimum

In the Windows Store app using JavaScript platform, the DOM and the JavaScript engine are separate components. Any JavaScript operation that involves communication between these components has a performance impact in comparison to operations that can be carried out completely in the JavaScript runtime. So it's important to keep interactions between these components to a minimum.

Use DOM objects only to store information that directly affects how the DOM lays out or draws elements. Using DOM objects can result in a 700 percent increase in access time as compared to accessing variables that aren't attached to the DOM. For more info, see Writing efficient JavaScript.

[Top]

Optimize property access

The flexibility of being able to add properties to and remove properties from individual objects on the fly results in a significant performance impact because property-value retrieval requires a dictionary lookup. You can speed up property access for certain programming patterns by using an internal inferred type system that assigns a type to objects that have the same properties. To take advantage of this optimization, you should:

  • Use constructors to define properties.
  • Add properties to objects in the same order, if you create multiple instances of an object.
  • Don't delete properties, because doing so can greatly degrade the performance of operations on the object that contained the property.
  • Don't define default values on prototypes or conditionally added properties. Though doing so can reduce memory consumption, such objects receive different inferred types, so accessing their properties requires dictionary lookups.

For more info, see Writing efficient JavaScript.

[Top]

Use independent animations

Windows Store apps using JavaScript allow certain types of animations to be offloaded from the UI thread to a separate, GPU-accelerated system thread. This offloading creates smoother animations because it ensures that the animations aren't blocked by the actions in the UI thread. This type of animation is called an independent animation.

For example, Hilo uses CSS3 transitions and animations to independently animate the transform property to rotate a photo. For more info, see Animating.

[Top]

Manage layout efficiently

To render an app, the system must perform complex processing that applies the rules of HTML, CSS, and other specifications to the size and position of the elements in the DOM. This process is called a layout pass, and it can have a performance impact.

A number of API elements trigger a layout pass, including window.getComputedStyle, offsetHeight, offsetWidth, scrollLeft, and scrollTop. One way to reduce the number of layout passes is to combine API calls that cause a layout pass. The more complex your app's UI, the more important following this tip becomes. For more info, see Managing layout efficiently.

[Top]

Store state efficiently

Store session data in the sessionState object. This object is an in-memory data structure that's good for storing values that change often but need to be maintained even if Windows closes the app. It's automatically serialized to the file system when the app is suspended, and automatically reloaded when the app is reactivated. By using this object, you can help to reduce the number of file operations that your app performs. For more info, see Storing and retrieving state efficiently.

[Top]

Keep your app's memory usage low when it's suspended

When your app resumes from suspension, it reappears nearly instantly. But when your app restarts after being closed, it might take longer to appear. So preventing your app from being closed when it's suspended can help to manage the user's perception and tolerance of app responsiveness. You can accomplish this by keeping your app's memory usage low when it's suspended.

When your app begins the suspension process, it should free any large objects that can be easily rebuilt when it resumes. Doing so helps to keep your app's memory footprint low, and reduces the likelihood that Windows will terminate your app after suspension. For more info, see Optimizing your app’s lifecycle and Handling suspend, resume and activation.

[Top]

Minimize the amount of resources that your app uses

Windows has to accommodate the resource needs of all Windows Store apps by using the Process Lifetime Management (PLM) system to determine which apps to close in order to allow other apps to run. A side effect of this is that if your app requests a large amount of memory, other apps might be closed, even if your app then frees that memory soon after requesting it. Minimize the amount of resources that your app uses so that the user doesn't begin to attribute any perceived slowness in the system to your app.

[Top]

Understanding performance

Users have a number of expectations for apps. They want immediate responses to touch, clicks, and key presses. They expect animations to be smooth. They expect that they'll never have to wait for the app to catch up with them.

Performance problems show up in various ways. They can reduce battery life, cause panning and scrolling to lag behind the user's finger, or make the app appear unresponsive for a period of time.

Optimizing performance is more than just implementing efficient algorithms. Another way to think about performance is to consider the user's perception of app performance. The user's app experience can be separated into three categories – perception, tolerance, and responsiveness.

  • Perception. User perception of performance can be defined as how favorably they recall the time it took to perform their tasks within the app. This perception doesn't always match reality. Perceived performance can be improved by reducing the amount of time between activities that the user needs to perform to accomplish a task.
  • Tolerance. A user's tolerance for delay depends on how long the user expects an operation to take. For example, a user might find cropping an image intolerable if the app becomes unresponsive during the cropping process, even for a few seconds. You can increase a user's tolerance for delay by identifying tasks in your app that require substantial processing time and limiting or eliminating user uncertainty during those tasks by providing a visual indication of progress. And you can use async APIs to avoid making the app appear frozen.
  • Responsiveness. Responsiveness of an app is relative to the activity being performed. To measure and rate the performance of an activity, you must have a time interval to compare it against. The Hilo team used the guideline that if an activity takes longer than 500 milliseconds, the app might need to provide feedback to the user in the form of a visual indication of progress.

[Top]

Improving performance by using app profiling

One technique for determining where code optimizations have the greatest effect in reducing performance problems is to perform app profiling. The profiling tools for Windows Store apps enable you to measure, evaluate, and find performance-related issues in your code. The profiler collects timing information for apps by using a sampling method that collects CPU call stack information at regular intervals. Profiling reports display information about the performance of your app and help you navigate through the execution paths of your code and the execution cost of your functions so that you can find the best opportunities for optimization. For more info, see How to profile JavaScript code in Windows Store apps on a local machine. To learn how to analyze the data returned from the profiler, see Analyzing JavaScript performance data in Windows Store apps.

When profiling your app, follow these tips to ensure that reliable and repeatable performance measurements are taken:

  • At a minimum, take performance measurements on hardware that has the lowest anticipated specifications. Windows 8 runs on a wide variety of devices, and taking performance measurements on one type of device won't always show the performance characteristics of other form factors.
  • Make sure that you profile the app on the device that's capturing performance measurements when it is plugged in, and when it is running on a battery. Many systems conserve power when running on a battery, and so operate differently.
  • Make sure that the total memory use on the system is less than 50 percent. If it's higher, close apps until you reach 50 percent to make sure that you're measuring the impact of your app, rather than that of other processes.
  • When you remotely profile an app, we recommend that you interact with the app directly on the remote device. Although you can interact with an app via Remote Desktop Connection, doing so can significantly alter the performance of the app and the performance data that you collect. For more info, see How to profile JavaScript code in Windows Store apps on a remote device.
  • Avoid profiling your app in the simulator because the simulator can distort the performance of your app.

[Top]

Other performance tools

In addition to using profiling tools to measure app performance, the Hilo team also used the Performance Analyzer for HTML5 Apps, and the Windows Reliability and Performance Monitor (perfmon).

The Performance Analyzer for HTML5 Apps is a tool that enables you to identify common performance issues in your HTML5 apps. The tool examines an app for common performance measures, like activation time, UI responsiveness, memory footprint, memory leaks, and memory growth. For more info, see Performance Analyzer for HTML5 Apps.

Perfmon can be used to examine how programs you run affect your device's performance, both in real time and by collecting log data for later analysis. The Hilo team used this tool for a general diagnosis of the app's performance. For more info, see Windows Reliability and Performance Monitor.

[Top]