Use ScenePulseListener to avoid expensive recalculations?
Martin Sladecek
martin.sladecek at oracle.com
Thu Nov 7 11:55:47 PST 2013
On 11/07/2013 04:03 PM, Tomas Mikula wrote:
> On Thu, Nov 7, 2013 at 3:34 PM, Martin Sladecek
> <martin.sladecek at oracle.com> wrote:
>> On 11/07/2013 03:18 PM, Tomas Mikula wrote:
>>> Hi Martin,
>>>
>>> On Thu, Nov 7, 2013 at 2:32 PM, Martin Sladecek
>>> <martin.sladecek at oracle.com> wrote:
>>>> This is something different. When properties depends on each other (using
>>>> bindings), the binding computation is deferred to the first query (get()
>>>> call) of the dependant. That means, if q depends on p, you can call
>>>> p.set()
>>>> as many times you want, but the recomputation will be triggered just
>>>> before
>>>> first q.get() call.
>>> The point of my first example was inconsistency.
>>> Suppose this scenario (remember, the invalidation listeners of p are
>>> deferred until the next pulse):
>>>
>>> [pulse]
>>> ...
>>> p.set()
>>> ...
>>> q.get() // returned value not consistent with the value of p
>>> ...
>>> [pulse]
>> It is consistent, providing that you use binding API for the computation.
> I probably forgot to state it explicitly that I'm considering an
> implementation of properties/bindings that don't trigger the listeners
> (invalidation/change) right away, but defer them until the next pulse.
> Then again, the state of that hypothetical object above is not
> consistent (until the next pulse).
>
>>> I like my objects to appear consistent to the outside world at all
>>> times, so that the client code doesn't have to deal with the
>>> inconsistencies.
>>>
>>>> Also in your second example, the second invalidation of
>>>> s would be practically a no-op.
>>> It is not if you have an invalidation (or change) listener registered on
>>> s.
>> Only in case of change listener. Invalidation listener would not
>> validate/recompute s.
> The invalidation listener will be executed twice. That itself is bad
> enough. The invalidation listener itself can trigger an expensive
> computation. Whether or not it (indirectly) causes the recomputation
> of s. Consider, for example, N such [p <- s] black boxes, where a
> single invalidation of p causes two invalidations of s, and chain them
> sequentially. Then a single invalidation of p_1 causes 2^N
> invalidations of s_N. That's bad enough, even though no property value
> is being recomputed. This is, in fact, the behavior of the bindings
> API you mentioned. What I'm saying is that deferring invalidation
> listeners until the next pulse doesn't help either (except for simple
> cases).
No, the invalidation listener will be executed only once. The property
needs to be validated before the invalidation listener can be called again.
-Martin
>
> Cheers,
> Tomas
>
>> -Martin
>>
>>> Tomas
>>>
>>>> Of course, this doesn't work if you have a ChangeListener on q, because
>>>> the
>>>> ChangeListener basically need to do get() in order to compute the new
>>>> property value (which is passed as an argument to it's method).
>>>>
>>>> When it's not a property you want to recompute, but an internal state
>>>> (you
>>>> use to setup the children), layoutChildren() should be the method for
>>>> you.
>>>>
>>>> -Martin
>>>>
>>>>
>>>> On 11/07/2013 02:08 PM, Tomas Mikula wrote:
>>>>> On Thu, Nov 7, 2013 at 11:58 AM, John Hendrikx <hjohn at xs4all.nl> wrote:
>>>>>> Hm, I found it googling, and since it showed up here:
>>>>>>
>>>>>>
>>>>>>
>>>>>> http://docs.oracle.com/javafx/2/api/javafx/scene/Scene.ScenePulseListener.html
>>>>>>
>>>>>> I figured it was public, but I just noticed the class is defined
>>>>>> package
>>>>>> private.
>>>>> Although not part of the public API, you can use
>>>>>
>>>>> import com.sun.javafx.tk.TKPulseListener;
>>>>> import com.sun.javafx.tk.Toolkit;
>>>>>
>>>>> Toolkit.getToolkit().addSceneTkPulseListener(new
>>>>> TKPulseListener(){...})
>>>>>
>>>>>
>>>>> Anyway, I don't think deferring property invalidation until the next
>>>>> pulse is very useful in general, for the following reasons:
>>>>>
>>>>> 1) It can lead to inconsistent observable state of your objects.
>>>>> Consider an object with properties p, q, where the value of q depends
>>>>> on the value of p, and consider changing the value of p. Now, right
>>>>> after
>>>>> p.set(x);
>>>>> returns, the state of the object is inconsistent until the next pulse,
>>>>> and this inconsistency is observable to the outside world.
>>>>>
>>>>> 2) It doesn't (in general) avoid recalculations. Consider properties
>>>>> p, q, r, s, whose invalidation listeners are deferred until the next
>>>>> pulse, with bindings
>>>>> p <- q <- r <- s
>>>>> p <- s
>>>>> and consider changing the value of p in pulse 0. The invalidation
>>>>> listeners will fire as follows:
>>>>> pulse 1: p
>>>>> pulse 2: q, s
>>>>> pulse 3: r
>>>>> pulse 4: s
>>>>> As you see, the listeners of s are called twice, causing potentially
>>>>> expensive recalculation.
>>>>>
>>>>> For these reasons, I'm in favor of the approach suggested by Yennick,
>>>>> where you "commit" the properties yourself.
>>>>>
>>>>> Regards,
>>>>> Tomas
>>>>
More information about the openjfx-dev
mailing list