Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
frederic_berg
Employee
Employee

Overview and motivation


I decided a while ago to write this blog post after a call I had with a customer who struggled with the startup performance of his SAPUI5 application. The initial steps taken to improve the situation actually worsened it and the reasons for this behavior were not understood.

The goal of this post is to outline some of the basic principles which need to be understood in terms how SAPUI5 loads its own resources and those of applications. Optimizing the choreography is key to getting the best possible startup performance. In addition, we will look at which effect a high latency can have on your performance and how to best tackle this issue as well using the AKAMAI network.

We will use a very simple application for this exercise, one which nonetheless has all of the key structures and artifacts found in "typical" applications and thus serves quite well to illustrate the mechanics.

You can get the sources of the sample application here:

https://github.com/Michadelic/ApplicationStartupPerformanceDemo

 

The initial startup time of this application is around 8s - we will optimize it to around 2s.

Understand the application structure


The application consists of all typical folders and their included artifacts.



  • controller: Javascript files with the controller implementations for the corresponding views

  • css: Application specific CSS files (if required)

  • i18n: Contains the properties files which contain the application's resource files in their respective translation

  • model: Application specific models and their respective modules for implementation

  • util: Typically a set of Javascript files needed by the application (folder name is not standardized, but most applications have such a folder with additional JS code)

  • view: Typically XML files for all view definitions

  • Component.js: Modern applications (which follow the official SAPUI5 best practices and all modern SAPUI5 Fiori applications) have this file which represents the application component

  • manifest.json: This file is the metadata description of the application and also found in most modern SAPUI5 applications



Understanding the bootstrapping within index.html

<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta charset="UTF-8">

<title>Performance Best Practices</title>

<script id="sap-ui-bootstrap"
src="../../resources/sap-ui-core.js"
data-sap-ui-libs="sap.m, sap.ui.table, sap.ui.commons, sap.ui.ux3"
data-sap-ui-theme="sap_bluecrystal"
data-sap-ui-compatVersion="edge"
data-sap-ui-preload="sync"
data-sap-ui-resourceroots='{"LatencyDemo": ""}'>
</script>

<script>

jQuery.sap.require('LatencyDemo.util.File1');
jQuery.sap.require('sap.ui.core.mvc.XMLView');

new sap.ui.xmlview({viewName:"LatencyDemo.view.View1"}).placeAt("content");

</script>
</head>

<body class="sapUiBody" id="content">
</body>

This is a pretty standard index.html content as you are sure to find it in many applications. There are certain things to notice however:

  1. Large list of libraries: 4 libraries are configured for immediate loading

  2. Synchronous Preload: data-sap-ui-preload="sync" is the default setting and most likely active in many applications (since the setting is most often omitted)

  3. Script: Direct usage of Javascript APIs in the inline script tag - this means a direct and synchronous processing after all previous scripts are processed by the browser


Now let's have a look at the network trace of this application when it's started (from a server with a somewhat high latency).


...



Notice the waterfall like loading of synchronous sequential requests of the core and all required libraries. 28 requests with a total time of about 8 seconds.

The contents of the libraries (usually a high number of individual Javascript files) are bundled into single files which are called library-preload.js (or JSON, depending on your UI5 version). These are the files which consume most of the initial startup time.

This is one of the performance anti-patterns which we need to avoid. Instead, we should try to load as many files in parallel as possible, which fortunately is supported by SAPUI5 when it comes to loading your required libraries.

Step 1: Loading SAPUI5 libraries asynchronously


Going back to the original reason for this post, the customer had also tried this. Let's do it just like they tried to fix the issue - by letting SAPUI5 load its resources asynchronously.

Here's how the application was changed:
    <script id="sap-ui-bootstrap"
src="../../resources/sap-ui-core.js"
data-sap-ui-libs="sap.m, sap.ui.table, sap.ui.commons, sap.ui.ux3"
data-sap-ui-theme="sap_bluecrystal"
data-sap-ui-compatVersion="edge"
data-sap-ui-preload="async"
data-sap-ui-resourceroots='{"LatencyDemo": ""}'>
</script>

Notice the change to the setting data-sap-ui-preload - now to "async". 

With this additional setting in place, let's check out the network trace again:



Oops. 94 requests with a total loading time of almost 19 seconds. 

(In the customer's scenario, the total loading time went up to about 150 seconds with a total of around 400 request.)

So what went wrong? The answer is in the script tag on the index.html page.
    <script>

jQuery.sap.require('LatencyDemo.util.File1');
jQuery.sap.require('sap.ui.core.mvc.XMLView');

new sap.ui.xmlview({viewName:"LatencyDemo.view.View1"}).placeAt("content");

</script>

Some background on how browsers work: A browser will process script tags in the HEAD tag of the page sequentially. This means that the first script tag which loads the SAPUI5 core.js file is loaded and executed first and the code above is executed directly afterwards.

Before we switched to the asynchronous loading, the first script tag was in charge or loading all required libraries and did this in a synchronous way. This means that the browser was blocked by this action and did not yet get to the inline script tag shown above.

This also means that all libraries were loaded and available to the application code when the script was reached, so that the jQuery.sap.require calls and other usages of the API could rely on already available functionality and modules.

Since we switched to asynchronous loading, the first script tag which loads the SAPUI5 Core is loaded and schedules the loading of all other libraries but does not wait for them to be fully available. Instead, its processing is eventually completed and the browser can continue with the next script tag - our inline script.

Since this code is now executed WHILE the browser is still waiting for the libraries to be fully loaded, any synchronous API which loads content from these libraries must attempt to fetch the required modules via a separate synchronous AJAX call.

And since all SAPUI5 modules typically require more modules, which in turn require even more modules and so on, a huge cascade of synchronous sequential requests is issued until the entire dependency chain is resolved and fully loaded. Remember, all of this is happening while the browser is trying to load the libraries in parallel.

So how can we avoid this situation? By using the right sync point.

Step 2: Using the event attachInit of the SAPUI5 core


 

SAPUI5 has an event to notify an application that all initially required libraries are fully loaded. Here's a code showing how to use it:
    <script>

sap.ui.getCore().attachInit(function(){
jQuery.sap.require('LatencyDemo.util.File1');
jQuery.sap.require('sap.ui.core.mvc.XMLView');

new sap.ui.xmlview({viewName:"LatencyDemo.view.View1"}).placeAt("content");
});

</script>

By wrapping our application code in the callback function passed to the attachInit event, we can ensure that all SAPUI5 libraries are available and thus can avoid the additional sync requests.

This is the new network trace:



Much better. 29 requests and fully loaded in a little over 6 seconds.

The SAPUI5 Core gets loaded first and ensures a parallel loading of all required libraries. In addition, the browser loads all required CSS files in parallel.

Still, the time it takes to start loading the application is rather high - let's look at some details of the request performance.



Notice the entry for "Waiting (TTFB)" - that's Time To First Byte. This delay of around 194ms is basically caused by the high network latency since I am running this particular application on a server halfway around the world.

In my initial example, the customer was hit by a latency of close to 500ms - for each request!

Step 3: Use the AKAMAI network to reduce latency effects


In order to ensure that all static SAPUI5 files are served with the lowest possible latency, SAPUI5 has teamed up with AKAMAI and provides SAPUI5 from the HANA Cloud Platform as a Content Delivery Network cached by AKAMAI.

Here's how to use it: Simply reference SAPUI5 from HCP (more on this here)
    <script id="sap-ui-bootstrap"
src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
data-sap-ui-libs="sap.m, sap.ui.table, sap.ui.commons, sap.ui.ux3"
data-sap-ui-theme="sap_bluecrystal"
data-sap-ui-compatVersion="edge"
data-sap-ui-preload="async"
data-sap-ui-resourceroots='{"LatencyDemo": ""}'>
</script>

With this in place, let's check the network trace again:



28 requests in a little over 4 seconds - and a low latency of only 34ms. Hooray!

This is already much better and will perform significantly better when the connection's latency is even higher than in my scenario. AKAMAI will always server all SAPUI5 related content from the nearest possible mirror.

Step 4: Optimizing the application resources


So far, we have only optimized the way our application loaded the static SAPUI5 content. Looking further down the network trace, we still see quite a high number of requests and the typical waterfall pattern:


Let's have another look at what is really happening. We can see that all application files are loaded sequentially which again is a performance anti-pattern.

The entire loading sequence is started with the following statement:
jQuery.sap.require('LatencyDemo.util.File1');

This synchronous API tells SAPUI5 to ensure that a particular file or module is available to be used within the application code. The module is either already known to SAPUI5 (then nothing happens on the network) or is it not. In the latter case, SAPUI5 will now issue a synchronous request to load this file.

In this particular case however, the contents of this file include the following line:
jQuery.sap.require('LatencyDemo.util.File2');

Upon receiving this file, SAPUI5 no needs to go out and fetch the next file - again a synchronous request. This can go on for quite some time depending on the dependency chain and its size.

Component Preload for Application Resources

SAPUI5 offers several ways to overcome this issue. All are based around the idea to bundle all of your application resources into one single file. This file can then be loaded early on to provide the content of all modules used within your application. Subsequent calls to jQuery.sap.require will now no longer result in requests to the server.

You currently have several ways to generate such a preload bundle:
- Using OpenUI5 tooling (see https://github.com/SAP/grunt-openui5#openui5_preload )
- Using the SAP Web IDE

In order to benefit from this preload, you need to ensure that you load the component.

Here is the updated code in the index.html file:
    <script>

sap.ui.getCore().attachInit(function(){
jQuery.sap.require('LatencyDemo.util.File1');

new sap.ui.core.ComponentContainer({
name : "LatencyDemo"
}).placeAt("content");

});

</script>

Notice how we now create a new Component Container (which in turn will load the corresponding component via the right name). This component now loads the component-preload file from the server.

Generating a component preload using the SAP Web IDE

You can easily create this file using a particular project setting in your SAP Web IDE. Simply go to the project settings of your application via the context menu:



Go to "Project Types" and select "SAPUI5 Client Build"



Save and close the settings.

The preload file will be generated when the application is deployed to the Hana Cloud Platform. You can trigger the deployment via the context menu.



Let see the network trace:



Notice that the component-preload file is loaded. But wait, there are still the requests to File1 and File2 sent across the network. Why?

 

Step 5: Using the init event of the Component


The content of the preload file is loaded via an asynchronous call. Thus, similarly to the loading of libraries from SAPUI5, we also need to ensure that a usage of the included modules is done after SAPUI5 has loaded this preload bundle. For libraries we used the attachInit event of the SAPUI5 core, for application a similar point in time if the init function of the Component itself (see Component.js).
		init: function() {
// call the base component's init function
UIComponent.prototype.init.apply(this, arguments);

jQuery.sap.require('LatencyDemo.util.File1');

// set the device model
this.setModel(models.createDeviceModel(), "device");
}

Moving the jQuery.sap.require call into this function, we can achieve the desired result.

We also need to remove the call from the index.html:
    <script>

sap.ui.getCore().attachInit(function(){

new sap.ui.core.ComponentContainer({
name : "LatencyDemo"
}).placeAt("content");

});

</script>

(Don't forget to deploy the application again to regenerate the preload file).

Here's the new network trace:



Notice: No application files are loaded any more. All views, controllers and other javascript files are fetched via the preload. Total time around 2s, 22 requests.

Conclusion
Our final application is now down to a load time of about 2s - while the preload file is still being loaded from a server halfway around the globe with a latency of close to 200ms. The overall loading time was reduced from 8 to around 2 seconds.

Here are the main improvements and key take aways:

  1. Always load all SAPUI5 libraries asynchronously

  2. Ensure the use the attachInit event before requiring SAPUI5 modules

  3. Use the AKAMAI network via the SAP HCP deployment if possible in your scenario to reduce the effects of latency

  4. Use application resource bundles

  5. Ensure to use the init event of the component before requiring additional application resources


I hope this guide contains some information which will help you to revisit your application and hopefully achieve a better startup performance as well.

Here's the link to the second post on advanced topics:
https://blogs.sap.com/2016/11/19/sapui5-application-startup-performance-advanced-topics/
28 Comments