Skip to main content

JavaFX Script tip: The Single Assignment per Method rule (and more)

Posted by opinali on June 17, 2009 at 8:35 AM PDT

Property binding is a great feature of JavaFX Script, but it's not without its issues, limitations or risks as you can see in recent posts. But even if perfect, no programming language feature exempts the programmer from learning how to use it optimally. There is an important rule that you must follow for binding. Check this code (simplified, from the JavaFX Balls benchmark):

public function move () {
    this._x += this._vx;

if (this._x < model.walls.minX) {
        this._vx = -this._vx;
        this._x += this._vx;
    } else if (this._x > model.walls.maxX - this._d) {
        this._vx = -this._vx;
        this._x += this._vx;
    }
    // Similar code for _y, _vy
}

What's wrong here? I didn't write the original version, but when I changed it (and introduced the problem), I was not fully aware of the consequences. The problem is that the properties _x and _y are assigned multiple times in the method. This is bad because these properties are bound elsewhere in the code:

function newBall () {
    def ball = Ball{};
    ball.view = ImageView {
       translateX: bind ball._x translateY: bind ball._y
       image: image

};
    ball.initialize();
    ball
}

When move() is invoked for some ball, the first thing it does is updating the _x and _y coordinates. These updates trigger binding notifications that force those bound expressions to be recomputed, changing the state of the ImageView nodes that are created by newBall() and included in the animation. But if the ball "hits" any border of the window, one or more of move()'s if statements will execute extra code that "bounces" the ball. The original code only changed the velocity variables _vx and _vy, but I decided to also adjust the coordinates so the balls wouldn't be positioned in irregular positions (like negative coordinates) even for a single frame. But when move() executes a second assignment to _x, for example, all properties that are bound to _x will be notified and evaluated again. This is duplicate work that surely costs some CPU cycles.

The solution is very simple: I only need to make sure that properties subject to binding are only assigned to at most once per method - the Single Assignment per Method (SAM) rule. This is easy to do with extra local variables:

public function move () {

var newX = this._x + this._vx;

if (newX < model.walls.minX) {
        this._vx = -this._vx;
        newX += this._vx;
    } else if (newX > model.walls.maxX - this._d) {
        this._vx = -this._vx;
        newX += this._vx;
    }

    this._x = newX;

    // Similar code for _y (with a temp newY), _vy
}

In the new code, binding notifications and updates will only be triggered at the end, when the properties _x, _y are sync'ed with the temp variables newX, newY.

The SAM rule is not really a new idea; it's not even specific to JavaFX Script. I do this a lot in Java due to concurrency, to guarantee both consistency and performance: if I have to assign new values to shared fields and this is expensive enough so I don't want to do everything in a big synchronized block, then I build the new values using temporary variables, and in the end I just assign the result to the field. With the extra bonus that, if it's a single field to update (or there are no ordering issues), I don't even need to synchronize the assignment step, so the code has zero locking overhead. I also follow the SAM rule for volatile fields even when the value computation is not expensive, because volatile writes are expensive. For really performance-critical stuff, I often obey the SAM rule even for common fields, because writing to any field is more expensive than to local variables, for two reasons: (a) local vars are typically allocated in registers so writes don't require memory stores, (b) for reference-type fields, most modern GCs impose "write barriers" - extra code that must be executed when any heap pointer is updated.

Last but not least, the SAM rule gives me a warm and fuzzy feeling of writing code closer to a "functional style", because even when I have to program with mutable state, at least I avoid temporary states (that may even be invalid). I prefer that objects transition from a canonical state A (a method's pre-condition) to a canonical state B (a method's post-condition) in a single shot, without any intermediary states. This is also good for concurrent code in situations where eventual races can be tolerated, except if they'd cause an exception or other misbehavior due to a thread using an object that is in an invalid, intermediary state.

Is the SAM rule good enough?

Now let's go back to my example. Pondering it more thoroughly, I could certainly make extra enhancements.

For one thing, I could store _x and _y in a Point2D object, say _pos, then move() would end with a single field update"... but my first instinct screamed: Don't do that, because this would impose an extra object allocation per call to move(), and JavaFX Balls is a cross-language benchmark, and I don't want to add any overheads (however small) that competing Bubblemark programs are not paying"... but, would this change really make the program slower? The extra allocation surely has some cost, but on the other hand, I'm coalescing binding notifications. If I have a single _pos property to change, its update will produce a single binding notification that will "fix" the values of both the translateX and translateY properties from in the ball nodes. The reduced effort of binding propagation may save enough CPU cycles to compensate the extra allocation - or even more, resulting in a net optimization.

This leads to a second rule, now specific to JavaFX Script: Minimize Updates to "˜Bindable' Properties. The article Performance Improvement Techniques for JavaFX Applications already offers a short tip "Avoid Unreasonable Bindings", and other authors have also warned against binding-happy code. So I'm just being more specific: binding is a great tool, and sometimes you can't avoid it, but you can do make it lighter, e.g. by grouping a set of bindable properties in a single object that is updated wholesale and generates a single round of updates to bound properties. [P.S.: I haven't yet validated the claim that this behavior will be any faster, or enough faster to compensate for some extra allocation - in JavaFX Balls those binding events are just insignificant to appear in the performance profile. Asserting this would require a special microbenchmark, maybe I'll write it eventually.]

I agree with others who pointed as a problem that in JavaFX Script, all visible properties are bindable; we could use a modifier like Fabrizio's suggested unbindable that disallows binding to a specific property, or some other form of control. Even good IDE support would help - I'd love a code editor that could flag, with special syntax highlight, properties that are being used in some binding expression; a browsing operation to navigate from a property to all expressions bound to it; or special performance warnings (in a tool like Checkstyle or even in javafxc) for methods that break the SAM rule, or (horror!!) update bindable properties inside a loop. Such tools would easily flag the most severe, binding-related, performance bugs in JavaFX apps.

An unbindable property could be compiled into leaner code, without the wrapper "location" objects introduced by javafxc. With the current language, my property declared as public var _x = 0.0 (with static type Float determined by inference) is compiled to a FloatVariable object (plus significant wrapping code for get/set and other things). This overhead apparently doesn't exist for local variables, non-public variables that are not bound, and constants declared by def. Still, it's significant overhead for all public var fields in your whole program; even in a best-case scenario where HotSpot optimizes out all boilerplate, the cost of extra memory allocation remains. So, a binding modifier would be a important optimization"... even if it's an "ugly" low-level optimization, much in the spirit of Java's final modifier. But when a language moves two steps forward in abstraction, it's often important to give developers some way to make at least one step backward in performance-critical code. There are also semantically valid usages of unbindable. Besides the problem discussed by Fabrizio's blog, an API provider may want to enforce "binding direction" in complex object graphs where developers could easily get confused and bind things the wrong way"... binding doesn't tolerate cycles, try this code: var x:Integer = bind y; var y = bind x.

.

Related Topics >>

Comments

@fabriziogiudici: +1 for that, I'm another Java developer who just can't live without both Checkstyle (always on) and FindBugs (at least frequent runs). No matter how good the current IDE plugins become, my JavaFX IDE will only be complete when I get similar lint tools. Binding is just the beginning; I can envision a whole category of best-practice or performance bug detections for sequences. And the nice thing of a higher-level language is that lint tools can be much smarter; they don't have to infer semantic meaning from low-level operations, because that semantics is explicit and hardwired to the language, so tools can rely on it. I expect for example that more advanced and precise warnings can be generated for JavaFX Script's sequences than it is possible for Java's collections.

... and what we, in general, need now is a FindBugs version for JavaFX. Obviously the language will change slowly and I expect mainly on a plan already sketched by Sun; IDEs can surely help, but if we had FindBugs pointing out bad practices, that would be a plus.