Wednesday, May 15, 2013

Performance Tuning On GWT Applications


Performance Tuning On GWT Applications

                            This article discuss about various aspects of tuning your app. I have placed few information from experience and read from an internet.

Common Causes of Performance Issues


                Following site impressed so much, which not restricted to GWT good guidelines for all web app.


Java Collections API to handle a large amount of data.

The Collections API on GWT is very slow when compared with native javascript arrays and maps. Even when you use java arrays, they are much slower than native javascript ones.

You can read more detail comparison report here.

To solve it, you can use Crux collection API. It uses the java Collections when running on Dev Mode and native arrays and maps for web mode.

or

GWT Lightweight Collections. Between other improvements they promised to bring minimum size of compiled script and absolute maximum speed. GWT Lightweight collections designed to be client-side only. If you will want to transfer those using RPC mechanism you will likely end in the exception.

In general to speed up your JavaScript is to use Arrays instead of Collections where it is possible both for transport and processing. Arrays are closer to its JavaScript analogues and GWT does not compile-in too much wrapping code for compatibility purposes.

RPC interfaces that declare very generic parameters

When you are designing interfaces to access the server side of application using GWT RPC, you must care with your parameters and exceptions types.

To implement your method calls, GWT needs to serialize the objects passed as parameters (and also return types, exceptions thrown, etc.). To do this, it creates TypeSerializers for those types.

Behind the scenes, you will have genereted javascript objects to represent your rpc classes. These objects will contain some maps. These maps contains entries pointing to the necessary TypeSerializers.

So if you declare something like:


1
2
3
4
5
6
7
8
9
10
11
package com.mycompany.client.remote;
 
import java.io.Serializable;
import java.util.List;
 
import com.google.gwt.user.client.rpc.RemoteService;
 
public interface MyRPCService extends RemoteService
{
 void myMethod(List< Serializable > parameter) throws Exception;
}

GWT will need to create serializers for all possible exceptions (all ones within your client folder), because your method can throw any exception. It will need to create serializers for all kind of Lists (ArrayList, LinkedList, etc) and for any class that implements Serializable.

It will generate a very big number of TypeSerializers and turn that javascript map of serializers, mentioned earlier, into a very big map, wasting memory and making your application slower.

You should declare your interfaces as much specific as possible, like on the example:


1
2
3
4
5
6
7
8
9
10
package com.mycompany.client.remote;
 
import java.util.ArrayList;
 
import com.google.gwt.user.client.rpc.RemoteService;
 
public interface MyRPCService extends RemoteService
{
 void myMethod(ArrayList< String > parameter) throws MyException;
}

Other important point is try to pass only the minimum required as parameters and return types. It will decrease the amount of data serialized and transmited through the network.

Excessive number of accesses to the page DOM


Especially in IE, access the DOM is very slow. Hence, we should try to code in order to reduce the number of accesses.

Whenever possible, use variables, as a form of cache to the DOM elements that are frequently accessed, avoiding run many calls to getElementById().

It is essential to monitor the behaviour of your application with a good profiler tool to identify how to reduce these accesses and manipulate the DOM in a proper way. We strongly suggest Dynatrace for it.

Even if you're not manipulating the DOM directly, you must be aware of this problem, since the way your code is written can change how the code generated by GWT accesses the DOM.

As an example, imagine a case where we need to create an HTMLPanel and add several widgets inside it, on specific positions. We can write this code like:
1
2
3
4
5
6
7
8
9
10
11
public void myMethod()
{
 HTMLPanel htmlPanel = new HTMLPanel(html);
  
 htmlPanel.add(new Button(), "myButton");
 htmlPanel.add(new TextBox(), "myTextBox");
 htmlPanel.add(new ListBox(), "myListBox");
 //other widgets ...
   
 RootPanel.get().add(htmlPanel);
}
And we can also write like:
1
2
3
4
5
6
7
8
9
10
public void myMethod()
{
 HTMLPanel htmlPanel = new HTMLPanel(html);
 RootPanel.get().add(htmlPanel);
  
 htmlPanel.add(new Button(), "myButton");
 htmlPanel.add(new TextBox(), "myTextBox");
 htmlPanel.add(new ListBox(), "myListBox");
 //other widgets ...
}


The two versions are very similar, but the performance is very different. The first version is much slower.

The reason for this is how the widget HTMLPanel works. The method add(Widget, String) adds the widget within the element with id entered as the second parameter of the method.

To find this element, the HTMLPanel runs the getElementById command. The point is that the HTMLPanel is not attached to the DOM yet, what would cause the method getElementById returns nothing. Thus, when the add method is called before the panel be attached to the DOM, it attaches itself to a temporary element in DOM, runs the getElementById method and then removes itself from the temporary element.

Thus, the first code shown generates much more changes in the DOM than the second (becoming slower), although the end result is the same.

You must also be careful when choosing the widgets to your page. To present a large amount of data, use the GWT new Cell widgets (in the place of the old Grids and Tables) that are much faster, because they build the widget HTML as a string before attach it, making much less accesses to DOM.

Build all widgets eagerly

A very common problem on applications is to build all widgets at the same time, when the page loads. It is a very bad practice that makes your application slow.

You must always render strictly what is necessary on each moment. Try to create your widgets lazily, when they become necessary.

Using GWT's LazyPanel you can make your application faster, by rendering some widgets only when they are needed. Widgets whose contents are conditionally visible (like DeckPanels and DisclosurePanels) can make an excellent usage of the feature provided by the lazy mechanism.

String handling is slow, particularly in Internet Explorer

               Even with the new StringBuffer class, you still need to be careful when dealing with strings. Some of the StringBuffer methods are implemented by calling toString() and doing something on the result. That can be a real performance killer. So anything you can do to stay away from substring()charAt(), etc. will help you. This can mean that it’s sometimes better to work with plain String instead of StringBuffer.

Lets see an example. We have some components that need to make a lot of string comparisons. These comparisons are made inside a very big loop.

When GWT compiles the code to javascript, it will produce the following code for the String.equals() method:

1
2
3
4
5
6
7
8
9
function java_lang_String_$equals__Ljava_lang_String_2Ljava_lang_Object_2Z(this$static,other)
{
 if(!(other != null &&
            com_google_gwt_lang_Cast_canCast__IIZ(other.java_lang_Object_typeId$, 1)))
 {
  return false;
 }
 return  String(this$static) == other;
}

Any javascript programmer would see this code and think: "To compare two strings I could just write one line of code and use the === operator!!!". Yes, it is wright, but GWT implements the method in order to make it compatible with the java API. The method equals() receives an Object as parameter, and it needs to handle any type differences. It checks if the parameter can be cast to String (if it is another String or a subclass of it.)

However, if we detected that the loop with comparations is creating a performance issue, we could use a trick. We could create a static method to do this comparation faster. See the following example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class StringUtils
{
  public static boolean unsafeEquals(String a, String b)
  {
   if (GWT.isScript())
   {
    return a==b;
   }
   else
   {
    return a.equals(b);
   }
  }
}

Then, we just change the loop to something like:


1
2
3
4
5
6
7
for (int i=0; i < veryBigIndex; i++)
 
 if (StringUtils.unsafeEquals(a, b))
 {
  // ...
 }
}

The result is that the equals method will be used on DevMode and the final javascript (for web mode) will be generated as:


1
2
3
4
5
6
7
for (var i=0; i < veryBigIndex; i++)
{
 if (a == b)
 {
  // ...
 }
}


We don´t need to care with type parameter differences because the unsafeEquals method signature declares its parameters as Strings, so the compiler will ensure the types compatibility, once String is a final class and can not be overridden.

Note that unsafeEquals is not checking for null parameters (that's why it is called "unsafe").

That example was extracted from a real case, where we obtain a considerable gain.

However, it is important to emphasize that we could not use this kind of trick indiscriminately. It would turn our code more complex to read and maintain  It must be used when we are faced with a performance issue, detected previously with a profiler tool.

Besides all the problems mentioned above, you need to remember that we are talking about web applications. The discussion on this post is focused on problems strictly related with GWT, but we also need to think on all aspects of the web application.
We need try to reduce the number of requests, use cache and expires headers properly, and follow all good practices related with this kind of development.

GWT has given few guidelines


Use profilers for analyzing size and performance

                  See my blog for "How to profile javascript using chrome.





2 comments:

  1. Hi, Great.. Tutorial is just awesome..It is really helpful for a newbie like me.. I am a regular follower of your blog. Really very informative post you shared here. Kindly keep blogging. If anyone wants to become a Front end developer learn from Javascript Online Training from India . or learn thru JavaScript Online Training from India. Nowadays JavaScript has tons of job opportunities on various vertical industry. ES6 Online Training

    ReplyDelete