One For the JavaFX Developer Crowd
Update: problem solved (see the comments) – thanks Sean!
OK – this post is just for the JavaFX developer crowd. It shouldn’t be taken as a benchmark of JavaFX current or future performance…
Last night, I tweeted about the results of a quick test I did as preparation for building what I hope will be an interesting JavaFX demo application. The reason I tweeted the results was because I found them a little surprising; and I thought people would be interested.
Now, a couple of people found the numbers hard to believe… So, here’s the code… It’s very simple: it just inserts a number of dynamically created Line nodes (with random co-ordinates) into the scene graph:
/*
* A minimal JavaFX program to test on-the-fly creation of
* new Line nodes, and their insertion into the scene graph.
*
* You can play with different values of "n" in the run function.
* The program will print out to the console the time in seconds
* that it takes to create the nodes.
*/
package minimalinsert;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.util.Math;
package def stage: Stage = Stage {
title: "Minimal Dynamic Line Node Creation"
width: 640
height: 480
scene: Scene {}
}
function run(args:String[]) {
var i = 0;
/*
* Set n to be the number of new Line nodes you want to add to the
* scene graph dynamically. Warning - when n gets more than a
* a few hundred, this code takes a *LONG* time to run
*
*/
var n = 100;
var t1: Double;
var t2: Double;
var time: Double;
t1 = java.lang.System.currentTimeMillis();
while (i < n) {
addNewLineDynamically();
i++;
}
t2 = java.lang.System.currentTimeMillis();
time = (t2 - t1)/1000.0;
println(time);
}
function addNewLineDynamically(): Void {
var x1: Number;
var x2: Number;
var y1: Number;
var y2: Number;
x1 = rand(0,640);
x2 = rand(0,640);
y1 = rand(0,480);
y2 = rand(0,480);
insert Line {
startX: x1, startY: y1
endX: x2, endY: y2
strokeWidth: 2
stroke: Color.BLACK
} into Main.stage.scene.content;
}
function rand(min:Number, max:Number): Number {
return min + ( Math.random() * (max - min) );
}
I see the following performance with different values of n:
- n=10 , 0.02 seconds
- n=50, 0.11 seconds
- n=100, 0.36 seconds
- n=200, 1.8 seconds
- n=300, 5.3 seconds
- n=400, 11.8 seconds
- n=500 22.5 seconds
- n=600 37.7 seconds
I assume other will get similar performance when they run the code…. All of which begs the question: what am I doing wrong? As I say – this is one for JavaFX developers…
Sean Christmann wrote:
Check this out.
var lines:Line[];
while (i < n) {
insert addNewLineDynamically() into lines;
i++;
}
Main.stage.scene.content = lines;
and have addNewLineDynamically() return a Line object now.
n = 600 0.397 seconds.
Now for the explanation( i think), I don't think javafx uses a retain mode renderer like Flash does. What this means in practice is that every manipulation you make to the scenegraph is calculated and/or rendered immediately, so you're exponentially increasing rendering time for each individual manipulation to the scenegraph.
Posted 15 Sep 2009 at 1:08 am ¶
simon wrote:
Aha – fantastic! Much appreciated!
Posted 15 Sep 2009 at 1:19 am ¶
Josh Marinacci wrote:
this is correct. It’s not adding to a sequence that takes so much time. It’s updating the scenegraph, or more specifically the upper JavaFX layers of the scenegraph. Most of the time it doesn’t matter (since you rarely add hundreds of nodes a second), but it still should be better. We are doing a ton of work to make this orders of magnitude faster at all levels of the stack. Stay tuned for future releases.
Posted 15 Sep 2009 at 5:20 am ¶
simon wrote:
Orders of magnitude increases in performance sounds good!
Posted 15 Sep 2009 at 8:50 am ¶
Osvaldo Doederlein wrote:
Additionally, you are inserting each elements with a separate call to a helper function, this creates extra cost to update the “lines” sequence. JavaFX Script’s sequences are immutable data structures, so operators like “insert” usually create a full new sequence which is normally very costly. But if you perform many mutating operations within a single loop or a single method, the compiler is smart and will optimize this by creating only one temporary sequence, doing all inserts/removes, and then replacing the contents of the sequence variable only once.
Posted 15 Sep 2009 at 6:51 pm ¶
Richard Bair wrote:
Ha, funny thing I was just writing the tests for this today since I’m actually working on this part of the code. Jonathan Giles pointed me at the discussion.
The primary culprit is that on the JavaFX side we are including circularity and duplication checks whenever you modify the group’s content.
Heck, I’ll write a blog and link to it.
Cheers
Richard
Posted 15 Sep 2009 at 10:46 pm ¶
Richard Bair wrote:
Hey Simon, here’s the link to my blog going into some of the details about why it stinks and what we’re doing about it:
http://fxexperience.com/2009/09/performance-improving-insertion-times/
Posted 15 Sep 2009 at 11:15 pm ¶
simon wrote:
@Osvaldo – thanks for the tip – sounds like a useful optimization trick toknow
@Richard
Cool! I’m looking forward to seeing the JavaFX scene graph getting “fixed up” over the next few releases…
Posted 16 Sep 2009 at 7:48 am ¶