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
qmacro
Developer Advocate
Developer Advocate
0 Kudos
Super post, thanks! Really enjoyed it over my coffee just now. Love the suspense and the storytelling too 🙂
MikeDoyle
Active Contributor
0 Kudos
Thanks so much Frederic for taking the time to get this down in blog form.  Your session at Tech Ed was a highlight for me, so it's great to have this blog as a reference.
Noel_Hendrikx
Active Contributor
0 Kudos
Thanks for the blog, it really helped me in understanding how SAPUI5 performance can be improved!
0 Kudos
Thanks for your insight into this subject. Great blog post which is going directly into my favorites folder as a reference document for all my SAPUI5 app development!
MattHarding
Active Contributor
Great post Frederic - type of enterprise thinking we need to see in more posts!

Now I have a question for those who are predominantly doing Fiori Launchpad developments on 1.38 or higher, using WebIDE to deploy new apps and potentially have a regional Gateway server that FLP runs on...
It looks like the main take away is the following line in the init of the Component file. Is this correct (since everything is else is taken care of)?
jQuery.sap.require('LatencyDemo.util.File1');

That said, if you use Asynchronous Module Definition (AMD) syntax in your code; should that be effectively (current as of today) best practice for your Fiori apps without needing to do this?

Cheers,
Matt
Former Member
0 Kudos
Nice one.
JoseMunoz
Active Participant
0 Kudos
Great blog!
frederic_berg
Employee
Employee
Hi Matt,

thanks for your comment! For applications which run primarily in the FLP context, there is another set of best practices which I will also write about. This post is simply the baseline which should be known to all developers.

When it comes to AMD syntax, the code I used above is outdated. I did this to reflect a lot of the code which is still present in applications today and to provide a way to still optimize it easily.

AMD syntax is of course the standard way of declaring and requiring modules nowadays, this will also be reflected in the next post.

Thanks again
Frederic
htammen
Active Contributor
0 Kudos
As far as I understand the documentation SAPUI5 does support AMD syntax but does not load required modules asynchronously.

Excerpt from sap.ui.define

sap.ui.define is designed to support real Asynchronous Module Definitions (AMD) in future,
although it internally still uses the the old synchronous module loading of UI5.
Callers of sap.ui.define therefore must not rely on any synchronous behavior that they might
observe with the current implementation.

 

 
0 Kudos
Must read for every UI5 developer!!!
udaykumar_kanike
Active Contributor
0 Kudos
Very useful tips to improve UI5 application's performance.
stefanusz
Explorer
0 Kudos
Hi there,

I'm trying to use the Component-preload to speed up the app.

I have successfully created the file and I can see from my network watcher, it loads it first successfully and it contains all the files within my webapp folder.

The problem is, it seems I still see the same amount of request being made?

Do anyone encounter the same problem?

Thanks!
former_member317094
Discoverer
0 Kudos
Very Helpful.. Thanks Frederic. 
frank_weigel
Employee
Employee
The most typical reason for that is a mismatch between the module names that the WebIDE build determines at build time and the names that are used to require the modules at runtime.

The Component-preload.js stores each module by its determined build-time name. At runtime, the framework looks the module up in the Component-preload.js with the name used at runtime. If both names don't match, the preload won't work and the framework will fallback to single requests.

I would suggest that you take a look into the generated Component-preload.js and see if the module names look familiar to you. If not, most often, a mismatch is caused by the use of sap.ui.localResource or jQuery.sap.registerModulePath in the code of the app. These methods allow a quite flexible mapping from module names to source folders, but the WebIDE tooling doesn't take these calls into account (the code analysis that would be necessary to do this would be rather complex and error-prone).

HTH, Frank
MikeDoyle
Active Contributor
0 Kudos
Had chance to work through this in detail today with a legacy UI5 application.  I'm using the personal edition of Web IDE with an on-premise ABAP box acting as UI5 repository.  It seems that the personal edition doesn't have the build option in the project settings?  I guess I will still have to use Grunt for the build.

Also, when it comes to loading the libraries, I'm using the UI5 cache buster and the following reference
 src="resources/sap-ui-cachebuster/sap-ui-core.js"

Doesn't that rule out the use of AKAMAI?

Thanks again for a great blog and I'm looking forward to the 'Fiori edition'!
mauriciolauffer
Contributor
0 Kudos
Looks like real AMD is coming! Take a look at the release note for the new version (SAPUI5 1.44.0).



[FEATURE] Core: support asynchronous loading of libraries

https://openui5.hana.ondemand.com/1.44.0/#releasenotes.html
mhappe
Participant
0 Kudos
Hi Frederic,

 

Great blog! Thnx!

Is it possible to get the benifts of AKAMI running SAPUI5 apps on premise using the launchpad? In this case the index.html of the particular app is not used.

I'm very interested in your answer.

Thanks in advance!

0 Kudos
Hi frederic.berg,

 

index.html file is not loaded for UI5 applications running in FLP (HCP) It loads directly from component.js / component-preloader.js

How can we maintain these bootstrap setting in manifest.json?.

Regards,

Gururaj
frederic_berg
Employee
Employee
0 Kudos
Hi Gururaj,

in this case you need to trust the environment which embeds your components to follow these principles. Please also check my send post on how to ensure that the information inside of the manifest.json should be set up to further optimize the preload handling. The FLP scenario is not as error prone as SAP can ensure that most optimizations happen automatically, only the app content and the manifest is relevant in this case.

 

https://blogs.sap.com/2016/11/19/sapui5-application-startup-performance-advanced-topics/

 

Thanks

Frederic
former_member186158
Active Participant
0 Kudos
Very helpful for performance tuning
Former Member
Hi Frederic,

Thanks for the great blogs on performance optimisation.

In continuation to the question from GuruRaj and your response, when we run the app from launchpad, the framework/environment loads the Component-preload.js asynchronously (as mentioned by you). However manifestFirst functionality and model preload (even when we specify preload="true" for models) is not working when we run the app from launchpad. Looks like for the model Preload to work, the manifestFirst is a pre-requisite and the framework/environment does not do that automatically. Also there is no sap.ui.component declaration in web ide project from available templates. Can you please suggest how to make the manifestFirst and model Preload work in this case when the app is executed from launchpad and there is no index.html.

 

Thanks,

Indrajit

 
damiancr
Participant
0 Kudos
Hi,

Did you find any solution to this question? We have exactly the same problem. Our custom apps in Fiori Launchpad are not using manifestFirst function by default and we don't find the way to make it work.

 

Thanks, Damián.
Jayakrishnan
Active Participant
0 Kudos
Hi Frederic,

Thank you for this blog. Do we need to specify any size limit on OData Consumption. As we are working in OData service from ECC (Gateway System),there are some constrain for us such as display the 1000 no of data in 10s and Detail within 2s like that.

 

Thank you,

Regards,

JK.

 
prashant9444
Advisor
Advisor
0 Kudos
Superb!

Thanks,

Prashant
sasi_reddy7
Participant
0 Kudos
Hi Frederic

 

Do you have any suggestions on how to implement the below bootstrapping where we point the SAPUI5 lib to AKAMIA network in SAP On-Premise Portal SAPUI5 Apps (without running into any Clickjacking issues by the browser)?

 

<!– Bootstrapping UI5 –> <script id=sap-ui-bootstrap src=https://sapui5.hana.ondemand.com/resources/sap-ui-core.js data-sap-ui-libs=sap.m data-sap-ui-theme=sap_belize data-sap-ui-compatVersion=edge data-sap-ui-preload=async data-sap-ui-language=en data-sap-ui-resourceroots={myapp: .}> </script>

 

Thanks

Sasi

 
shais
Participant
0 Kudos
How/why using SAPUI5 CDN expose you to clickjacking vulnerability?
sasi_reddy7
Participant
0 Kudos
Since we are trying to access a different host (https://sapui5.hana.ondemand.com) in our app (which would be a different host), the browser throws the clickjacking error.
shais
Participant
0 Kudos
It sounds like a CORS issue, and not clickjacking one...