Profiling JavaFX Mobile applications

Today is a great day for every developer of JavaFX Mobile applications. You wonder why? Because the JavaME SDK 3.0 was released. It was long, hard work, from what I heard during our lunch breaks, but the result is a fantastic tool. Congratulations to the whole team and I am looking forward to celebrate this launch with you guys! 🙂

Some people might be wondering now, JavaME SDK – JavaFX Mobile, where is the connection? The JavaME SDK finally enables a so far well hidden functionality of JavaFX Mobile: profiling – which makes it the most important tool for JavaFX Mobile developers in my opinion. (Ok, maybe I am a little biased here, because performance is my day-to-day job…) 😉

Oh, yea, right. The JavaME SDK is also a great tool for developing JavaME applications – at least that’s what I heard. 😛

The remainder of this article will explain, how you enable the profiler, what you have to consider while profiling and finally how to view the results.

Enabling the profiler

To enable profiling of a JavaFX Mobile application, you need to change the settings of the VM. This is conveniently possible simply by changing the properties of one of the predefined devices (Alternatively you can define a new device explicitly for profiling.) To enable profiling with the default device DefaultFxPhone1, open the file device.properties in ~/javafx-sdk/1.1/work/0, which is located in your home-folder. You need to start the emulator at least once, so that the folder and file are created. If you look at the content, it will look similar to this:

#
# Copyright (c) 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
phone.number: 123456789
runtime.internal.com.sun.io.j2me.apdu.hostsandports = localhost:9025,localhost:9026
profiler.enabled: false
profiler.file: data.prof
netmon.enabled: false
runtime.internal.JAVA_HEAP_SIZE: 15728640
runtime.internal.MAIN_MEMORY_CHUNK_SIZE: 26214400
runtime.internal.microedition.locale: en-US

File Content of device.properties

For profiling we are only interested in the properties profiler.enabled and profiler.file. Enable profiling by setting the flag profiler.enabled:

profiler.enabled: true

The property profiler.file determines where the profiling data will be stored. If you do not change the default, it will be stored in the file data.prof in the same directory as device.properties. After you have changed the properties, you have to restart the emulator and the device-manager.

Running a profiling-session

Whenever you run an application in the emulator now, it will be profiled. After the application finishes, the result will be stored in the file which is configured in device.properties. Be aware, if you run two applications, the finishing the second one will overwrite the profiling data of the first application, so be sure to copy the file before running the second application.

While profiling, two issues need to be considered. First of all an application, which is profiled, runs extremely slow. In fact it runs so slow, that any user interaction is very difficult, if not impossible. The best option is to make your test run fully automated without the need of user interaction. The slow execution also affects animations, almost all frames will be dropped when profiling. You can change the duration of an animation if it is important to execute more frames.

The other issue to consider is, that the VM needs some time to write the profiling data to the file system after your application finishes. If you close the emulator window directly, the VM will be shutdown immediately and the file with the profiling data is usually corrupted.

One solution to overcome this is to make sure the application finishes by itself. You can call FX.exit() to quit a JavaFX application anytime. If you need to stop the application manually, press the red cancel button on the device. This will put the JavaFX application in the background and show the AMS (Application Management System). From there you can end the application without stopping the VM by selecting the running application and selecting “End” from the menu.

Viewing the profiling data

This is were the JavaME SDK finally comes into play. Start the SDK and select the entry “Import Java ME SDK snapshot…” from the menu Tools to load the file generated in your profiling session. This will open a view similar to the profiler window in NetBeans and give you an easy to use representation of the generated data.

Best Practices for JavaFX Mobile Applications 5

Today’s post focuses on optimizations for the core JavaFX runtime.

WARNING: The tips I am giving here are true for the current version of JavaFX Mobile, which is part of the JavaFX 1.1 SDK. In future versions the behavior will change, the
current bad performance of the mentioned artifacts will be optimized away or at least significantly improved. Everything I am writing about here is a snap-shot, nothing should be understood as
final!

Item 7: Define variables with def instead of var. Make them script-private.

When defining instance variables, it is good practice to limit the accessibility as much as possible. Also if a variable is immediately initialized and not reassigned afterwards, one should use the keyword def to define it. This is true for almost all bound variables, because bound variables cannot be reassigned (there is no such thing as an unbound-operation) and usually one already knows what they are bound to when they are defined.

Besides resulting in clearer and less error-prone code, following these suggestions also improves performance. The more hints we can provide to the compiler, the more aggressive it can optimize our code. Let’s take a look at an example in Code Sample 1.

1 class Main {
2     def i1: Integer = 0;
3     var i2: Integer;
4     public def i3: Integer = 0;
5     public var i4: Integer;
6 }

Code Sample 1: A sample script with public, private defs and vars

Code Sample 1 defines a small class with four members i1, i2, i3, and i4. The variables i1 and i2 are script-private, i3 and i4 are public; the variables i1 and i3 are defined with def, i2 and i4 are defined with var. Code Sample 2 shows part of the generated Java code.

1 class Main extends java.lang.Object implements Main$Intf,com.sun.javafx.runtime.FXObject{
2     public int $Main$i1;
3     public int $Main$i2;
4     public int $i3;
5     public final com.sun.javafx.runtime.location.IntVariable $i4;
6     ...
7 }

Code Sample 2: Parts of the generated Java code from Code Sample 1

What is remarkable about the generated Java code is the fact that all variables but i4 became simple ints; only the variable i4 is translated into an IntVariable, because it needs to provide more functionality. An int-variable requires less memory and performs faster than an instance of IntVariable.

Item 8: Use Integer instead of Number

Operations on integers are always faster than operations on floating-point values. On limited device, which usually do not have a mathematical coprocessor like desktop computers, the difference is exorbitant. Therefore it is good practice to use Integers whenever possible.

The type-inference mechanism of the JavaFX compiler usually does a very good job in determining the correct type of a variable, but if in doubt, it will chose Number. Therefore one should always set the type of Integer variables explicitly.

Item 9: Use functions of class Sequences

The class Sequences in the package javafx.util provides a large number of useful functions for dealing with sequences. One should be familiar with the provided functions and use them instead of implementing them by oneself. The functions in Sequences have been tested thoroughly and the performance is at least as good, if not better than that of own implementations.

Links to previous parts:

Item 1: Avoid unnecessary bindings
Item 2: Keep the scenegraph as small as possible
Item 3: Use simple shapes instead of images
Item 4: Use small images instead of complex shapes
Item 5: Use the prescaling functionality
Item 6: Use background loading

Best Practices for JavaFX Mobile Applications 4

The fourth part of my series about best practices for JavaFX Mobile Applications focuses on image loading.

WARNING: The tips I am giving here are true for the current version of JavaFX Mobile, which is part of the JavaFX 1.1 SDK. In future versions the behavior will change, the
current bad performance of the mentioned artifacts will be optimized away or at least significantly improved. Everything I am writing about here is a snap-shot, nothing should be understood as
final!

Item 5: Use the prescaling functionality

If an image needs to be scaled and the scaling factor does not change later, it is recommended to use the prescaling functionality. This can be done by setting width and height of an Image object, which will scale the image while it is loaded.

Using prescaling has two benefits. First of all it results in better performance. If prescaling is used, the scaling is definitely calculated only once. In contrast the scaling of an ImageView object is recalculated each time its transformation is changed by something else then a translation. For example changing the rotation will result in recalculating the scaling. Secondly, if an image is down scaled, memory usage is much smaller if prescaling is used.

Scaling can be calculated even faster if the flag Image.smooth is false. But the quality of the scaled image must be checked.

Example

This example generates thumbnails for a number of images. Code Example 1 creates a sequence of thumbnails, using the scaling functionality of ImageView.

1 def thumbnails = for (i in [0..11])
2     ImageView {
3         image: Image {url: "{__DIR__}images/img{i}.png"}
4         preserveRatio: true
5         fitWidth: 30
6         fitHeight: 20
7     }

Code Sample 1: Scaling within ImageView

The same can be achieved using the prescaling functionality of the Image class as shown in Code Example 2. Displaying the thumbnails is usually faster with this approach and the memory usage is much smaller.

1 def thumbnails = for (i in [0..11])
2     ImageView {
3         image: Image {
4             url: "{__DIR__}images/img{i}.png"
5             preserveRatio: true
6             width: 30
7             height: 20
8         }
9     }

Code Sample 2: Prescaling with Image

Item 6: Use background loading

The class Image provides a nice, but easily overlooked feature, to load images asynchronously in the background. This will not speed up the runtime performance or reduce the footprint of an application, but it can significantly improve startup time. To enable it, the flag Image.backgroundLoading must be set.

Background loading has two consequences, which need to be considered during implementation. If an image which is loaded in the background is supposed to be displayed shortly after creation, it is necessary to check the progress of the download. Otherwise an empty image will be shown first. Another option is to set the variable placeholder to display a substitute-image until the real image is finished loading. This approach is used in the example below.

The second consequence is, that width and height of an image are not set until the image is completely loaded. This will probably spoil any layout, which depends on the size of the used images. Again, a placeholder image can be used to overcome this, if placeholder-image and final image have the same size. Or width and height can be set manually, which would prescale the image to the given size. The last option is to recalculate the layout once the image is finished loading.

Example

Code Sample 3 extends the example from above to load the thumbnails in the background and displays them. While the images are loaded a placeholder (logo.png) is displayed, which has the same size as the thumbnails. Note that the logo is not loaded in the background to make sure we can display it immediately.

 1 def logo = Image {url: "{__DIR__}images/logo.png"}
 2
 3 def thumbnails = for (i in [0..11])
 4     ImageView {
 5         image: Image {
 6             url: "{__DIR__}images/img{i}.png"
 7             preserveRatio: true
 8             width: 30
 9             height: 20
10             backgroundLoading: true
11             placeholder: logo
12         }
13         x: i mod 4 * 50 + 20
14         y: ((i/4) as Integer) * 40 + 20
15 }
16
17 Stage {scene: Scene {content: thumbnails}}

Code Sample 3: Loading thumbnails in the background

On the emulator one has to look really close to notice the background loading. On a real device loading the images usually takes longer. With background loading enabled, the screen is displayed quickly, first showing only the placeholders, which are one after the other replaced with the real images. If background loading is disabled, the application would show a blank screen until all images are completely loaded and displayed.

Links to previous parts:

Item 1: Avoid unnecessary bindings
Item 2: Keep the scenegraph as small as possible
Item 3: Use simple shapes instead of images
Item 4: Use small images instead of complex shapes

Best Practices for JavaFX Mobile Applications 3

WARNING: The tips I am giving here are true for the current version of JavaFX Mobile, which is part of the JavaFX 1.1 SDK. In future versions the behavior will change, the
current bad performance of the mentioned artifacts will be optimized away or at least significantly improved. Everything I am writing about here is a snap-shot, nothing should be understood as
final!

Item 3: Use simple shapes instead of images

Item 4: Use small images instead of complex shapes

These two items seem to contradict each other. Unfortunately there is no easy answer available here:  sometimes using shapes is better, sometimes using an image is better. To help making the right decision, here are some points one should consider:

  • Complexity Single basic shapes like rectangles or circles are almost always faster than images. But the higher the number of shapes, which are assembled to get the desired artifact, or the more complex a user-defined path is, the more expensive operations on these shapes are. And the advantage shrinks. Important note: A javafx.text.Text object is a very complex shape.
  • Size The performance of most operations on images behaves quadratic, which means the operations become 4 times as slow if width and height are doubled, 9 times as slow if they are tripled etc. Therefore the bigger an element is, the better is using shapes.
  • Transformations Rotating or scaling does not only look better when using shapes, but it is usually also faster than transforming images. Especially if rotation and scaling are animated, shapes will perform better.
  • Startup time Loading an image and setting up an ImageView is usually slower then setting up shapes.
  • Footprint Static and dynamic footprint is almost always higher when using images.

Important: The variable cache of javafx.scene.Node is currently NOT used in the runtime. Setting it makes no difference!

Links to previous parts:

Item 1: Avoid unnecessary bindings
Item 2: Keep the scenegraph as small as possible

Best Practices for JavaFX Mobile Applications 2

Today I am writing about the second tip to increase performance of JavaFX Mobile applications. I think this and the previous tip are the most important ones.

WARNING: The tips I am giving here are true for the current version of JavaFX Mobile, which is part of the JavaFX 1.1 SDK. In future versions the behavior will change, the
current bad performance of the mentioned artifacts will be optimized away or at least significantly improved. Everything I am writing about here is a snap-shot, nothing should be understood as
final!

Item 2: Keep the scenegraph as small as possible

Behind the scenes of the runtime a lot of communication takes place to update the variables of the nodes in a scenegraph. The more elements a scenegraph has, the more communication is required. Therefore it is critical to keep the scenegraph as small as possible. Especially animations tend to suffer from a large scenegraph.

It is bad practice to keep a node in the scenegraph at all times and control its visibility via the visible-flag or its opacity. Invisible nodes in the scenegraph are still part of the communication-circus in the background. Instead one should remove nodes from the scenegraph and add them only when required.

This approach has one drawback though. Adding or removing nodes takes longer than setting the visibility. Therefore it might not be appropriate in situations were immediate responses are critical.

Example 1

Often one has a set of nodes of which only one is visible. These can be for example different pages, or nodes to visualize different states of an element. One might be tempted to add all nodes to the scenegraph and set only the current as visible.

Code Sample 1 shows a simplified version of this approach. Three colored circles are created to visualize some kind of state (red, yellow, green). Only one node is visible at any time. (Let’s ignore for a second that this could simply be achieved by changing the fill-color of a single circle. In real life applications one would probably have images or more complex shapes for visualizations and simply changing the color would not work.)

 1 def colors = [Color.GREEN, Color.YELLOW, Color.RED];
 2
 3 var state: Integer;
 4
 5 Stage {
 6     scene: Scene {
 7         content: for (i in [0..2])
 8             Circle {
 9                 centerX: 10
10                 centerY: 10
11                 radius: 10
12                 fill: colors[i]
13                 visible: bind state == i
14             }
15     }
16 }

Code Sample 1: Using visibility to switch between nodes

This results in three nodes in the scenegraph although only one is shown. This should be refactored to ensure that only the visible node is in the scenegraph. Code Sample 2 shows one possible implementation.

 1 def colors = [Color.GREEN, Color.YELLOW, Color.RED];
 2
 3 var state: Integer on replace oldValue {
 4     insert nodes[state] into stage.scene.content;
 5     delete nodes[oldValue] from stage.scene.content;
 6 }
 7
 8
 9 def nodes = for (i in [0..2])
10     Circle {
11         centerX: 10
12         centerY: 10
13         radius: 10
14         fill: colors[i]
15     }
16
17 def stage = Stage {scene: Scene{}}

Code Sample 2: Adding and removing nodes when required

The code in Code Sample 1 is more compact, but Code Sample 2 reduced the number of nodes in the scenegraph from three to one. While tuning some of the demos for the JavaFX Mobile release, we were able to reduce the number of nodes in the scenegraph by 50% and more, simply by ensuring that only visible nodes are part of it.

Example 2

If nodes are shown and hidden with some kind of animation, adding and removing the node to the scenegraph becomes extremely simple. One only needs to implement an action at the beginning of the fadeIn-animation and at the end of the fadeOut-animation to add respectively remove the node. Code Sample 3 shows such a usage where a simple message-box is shown and hidden by changing the opacity.

 1 def msgBox = Group {
 2     opacity: 0.0
 3     content: [
 4         Rectangle {width: 150, height: 40, fill: Color.GREY},
 5         Text {x: 20, y: 20, content: "Hello World!"}
 6     ]
 7 }
 8
 9 def fadeIn = Timeline {
10     keyFrames: [
11         KeyFrame {
12             action: function() {insert msgBox into stage.scene.content}
13         },
14         at (1s) {msgBox.opacity => 1.0 tween Interpolator.LINEAR}
15     ]
16 }
17
18 def fadeOut = Timeline {
19     keyFrames: KeyFrame {
20         time: 1s
21         values: msgBox.opacity => 0.0 tween Interpolator.LINEAR
22         action: function() {delete msgBox from stage.scene.content}
23     }
24 }
25
26 def stage = Stage {scene: Scene{}}

Code Sample 3: Using fadeIn- and fadeOut-animations to add and remove nodes.

Best Practices for JavaFX Mobile Applications

As everybody who is interested in JavaFX will know by now, JavaFX Mobile was released a short while
ago. It was a hell of a ride, that’s for sure. I felt so exhausted, I did not even have the energy to blog during the release…

But by now I feel recovered and want to start a little series about lessons we have learned while preparing the release and give some hints how to improve the performance of JavaFX Mobile applications.

WARNING: The tips I am giving here are true for the current version of JavaFX Mobile, which is part of the JavaFX 1.1 SDK. In future versions the behavior will change, the
current bad performance of the mentioned artifacts will be optimized away or at least significantly improved. Everything I am writing about here is a snap-shot, nothing should be understood as
final!

Item 1: Avoid unnecessary bindings

Bindings are very convenient, without any doubt one of the most valuable innovations in JavaFX Script. Unfortunately they come with a price. The generated boiler-plate code is usually not as small and
fast as a manual implementation would be. Especially complex dependency-structures tend to impose a severe penalty on performance and footprint.

For this reason it is recommended to avoid bindings as much as possible. Often the same functionality can be implemented with triggers. One should not use bindings to avoid the hassle of dealing with the initialization order. And it certainly makes no sense to bind to a constant value.

Lazy bindings are most of the time (but not always!) faster if a bound variable is updated more often then read, but they are still not as fast as manual implementations.

Example

A common use-case is a number of nodes which positions and sizes depend on the stage-size. A typical implementation uses bindings to achieve that.

Here we will look at a simple example, which resembles such a situation. The scene consists of three rectangles which are laid out diagonally from the top-left to the bottom-right. The size of the rectangle is a quarter of the screen-size. Code Sample 1 shows an implementation with bindings.

 1 def rectangleWidth: Number = bind stage.width * 0.25;
 2 def rectangleHeight: Number = bind stage.height * 0.25;
 3
 4 def stage: Stage = Stage {
 5     scene: Scene {
 6         content: for (i in [0..2])
 7             Rectangle {
 8                 x: bind stage.width * (0.125 + 0.25*i)
 9                 y: bind stage.height * (0.125 + 0.25*i)
10                 width: bind rectangleWidth
11                 height: bind rectangleHeight
12             }
13     }
14 }

Code Sample 1: Layout calculated with bindings

The first question one should think about is wether the bindings are really necessary. On a real device the screen-size changes only when the screen orientation is switched (provided that the device supports this functionality). If our application does not support screen rotation, the layout can be defined constant.

One possible solution to reduce the number of bindings is shown in Code Sample 2. Two variables width and height are introduced and bound to stage.width and stage.height respectively. Their only purpose is to provide triggers for stage.width and stage.height, since we do not want to override the original triggers. Position and size of the rectangles are calculated manually in the triggers.

 1 def r = for (i in [0..2]) Rectangle {}
 2
 3 def stage = Stage {
 4     scene: Scene {content: r}
 5 }
 6
 7 def height = bind stage.height on replace {
 8     def rectangleHeight = height * 0.25;
 9     for (i in [0..2]) {
10         r[i].height = rectangleHeight;
11         r[i].y = height * (0.125 + 0.25*i)
12     }
13 }
14
15 def width = bind stage.width on replace {
16     def rectangleWidth = width * 0.25;
17     for (i in [0..2]) {
18         r[i].width = rectangleWidth;
19         r[i].x = width * (0.125 + 0.25*i)
20     }
21 }

Code Sample 2: Layout calculated in trigger

Without any doubt, the code in Code Sample 1 is more elegant. But measuring the performance of both snippets in the emulator, it turned out the code in Code Sample 2 is almost twice as fast.