*PropertyBase vs Simple*Property
Tom Schindl
tom.schindl at bestsolution.at
Mon Mar 24 22:36:03 UTC 2014
The code I run is attached in the mail copy it to your env and run it
and flip the testLambda from true to false.
I might have been something dumb wrong but this is what I came up with.
Tom
On 24.03.14 23:31, Kevin Rushforth wrote:
> Those results are surprising. Is this an apples-to-apples comparison
> with the only difference being a Lambda versus an equivalent anonymous
> inner class?
>
> -- Kevin
>
>
> Tom Schindl wrote:
>> Hi,
>>
>> I've written a small sample to see what it gets me to check:
>> * creation overhead
>> * memory overhead
>> * call overhead
>>
>> I'm not very good at this kind of thing so someone who knows to write
>> benchmarks might know a lot better - need to check out JMH most likely.
>>
>> Anyways here are the numbers:
>>
>> Topic Lambda Subclass
>> --------------------------------------------------------------
>> Create10M 372ms (0.00003723) 220ms (0.00002205)
>> Mem 108byte / instance 84byte / instance
>> Call-1M*10 42ms (0.0000042) 35ms (0.0000035)
>> Call-1*1M 11ms (0.0000011) 10ms (0.0000010)
>>
>> So Lamda is considerable slower 40% and takes 20% more space, call
>> behavior is fairly the same. I'll try to learn about JMH.
>>
>> Tom
>>
>>
>>
>>> package hello;
>>>
>>> import java.util.ArrayList;
>>> import java.util.List;
>>> import java.util.concurrent.atomic.AtomicInteger;
>>> import java.util.function.Consumer;
>>>
>>> import javafx.beans.property.ObjectProperty;
>>> import javafx.beans.property.ObjectPropertyBase;
>>> import javafx.beans.property.SimpleObjectProperty;
>>>
>>> public class TestMemory {
>>> private static int oneIteration = 1_000_000;
>>> private static int iterationCount = 10;
>>> private static int invokationOverheadCallCount = 1_000_000;
>>>
>>> private static boolean testLambda = false;
>>>
>>> private static void testLambda(int iterations, List<TestObject> storage) {
>>> for( int i = 0; i < iterations; i++ ) {
>>> storage.add(new SimpleLambdaBean());
>>> }
>>> }
>>>
>>> private static void testSubclass(int iterations, List<TestObject> storage) {
>>> for( int i = 0; i < iterations; i++ ) {
>>> storage.add(new SimpleSubclassBean());
>>> }
>>> }
>>>
>>> public static void main(String[] args) {
>>> System.err.println("Test Creation time");
>>> System.err.println("==================");
>>>
>>> {
>>> long timeDiffTotal = 0;
>>> for( int i = 0; i < iterationCount; i++ ) {
>>> System.err.println(" Working for objects: " + (i * oneIteration) + " - " + ((i+1) * oneIteration) );
>>> System.err.println(" ---------------------------------");
>>> long s = System.currentTimeMillis();
>>> if( testLambda ) {
>>> testLambda(oneIteration, new ArrayList<>());
>>> } else {
>>> testSubclass(oneIteration, new ArrayList<>());
>>> }
>>> long e = System.currentTimeMillis();
>>> long diff = e - s;
>>>
>>> timeDiffTotal+=diff;
>>> System.err.println(" Creation time: " + diff + "("+diff * 1.0 / oneIteration+")");
>>>
>>> System.err.println(" ---------------------------------");
>>> }
>>>
>>> System.err.println(" Average time: " + timeDiffTotal * 1.0 / (iterationCount * oneIteration));
>>> }
>>>
>>> List<TestObject> target = new ArrayList<TestObject>(iterationCount * oneIteration);
>>>
>>> {
>>> System.err.println("");
>>> System.err.println("Test Creation memory");
>>> System.err.println("==================");
>>>
>>> for( int i = 0; i < iterationCount; i++ ) {
>>> System.err.println(" Working for objects: " + (i * oneIteration) + " - " + ((i+1) * oneIteration) );
>>> System.err.println(" ---------------------------------");
>>> if( testLambda ) {
>>> testLambda(oneIteration, target);
>>> } else {
>>> testSubclass(oneIteration, target);
>>> }
>>>
>>> long freeDiff = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
>>> System.err.println(" Memory: " + freeDiff + "("+freeDiff * 1.0 / target.size()+")");
>>> System.err.println(" ---------------------------------");
>>> }
>>>
>>> System.err.println(" Total objects created: " + target.size());
>>> }
>>>
>>> {
>>> System.err.println("");
>>> System.err.println("Test invokation overhead (all then times)");
>>> System.err.println("=========================================");
>>>
>>> long s = System.currentTimeMillis();
>>> for( int i = 0; i < oneIteration; i++ ) {
>>> target.get(i).invalidate();
>>> }
>>> long e = System.currentTimeMillis();
>>> long diff = e - s;
>>> System.err.println(" Total time calls: " + diff);
>>> System.err.println(" Time per call: " + diff * 1.0 / invokationOverheadCallCount);
>>> }
>>>
>>> {
>>> System.err.println("");
>>> System.err.println("Test invokation multiple times");
>>> System.err.println("===============================");
>>> long s = System.currentTimeMillis();
>>>
>>> for( int i = 0; i < invokationOverheadCallCount; i++ ) {
>>>
>>> }
>>> long e = System.currentTimeMillis();
>>> long diff = e - s;
>>> System.err.println(" Total time calls: " + diff);
>>> System.err.println(" Time per call calls: " + diff * 1.0 / invokationOverheadCallCount);
>>> }
>>>
>>> }
>>>
>>> public static class LamdaInvalidationProperty<T> extends SimpleObjectProperty<T> {
>>> private Consumer<LamdaInvalidationProperty<T>> c;
>>>
>>> public LamdaInvalidationProperty(Object bean, String name, Consumer<LamdaInvalidationProperty<T>> c) {
>>> super(bean, name);
>>> this.c = c;
>>> }
>>>
>>> @Override
>>> protected void invalidated() {
>>> c.accept(this);
>>> }
>>> }
>>>
>>> public interface TestObject {
>>> public void invalidate();
>>> }
>>>
>>> public static class SimpleLambdaBean implements TestObject {
>>> private AtomicInteger i = new AtomicInteger();
>>>
>>> private ObjectProperty<Object> sample = new LamdaInvalidationProperty<>(this, "sample", (e) -> {
>>> i.incrementAndGet();
>>> });
>>>
>>> public void invalidate() {
>>> sample.setValue(new Object());
>>> }
>>> }
>>>
>>> public static class SimpleSubclassBean implements TestObject {
>>> private AtomicInteger i = new AtomicInteger();
>>>
>>> private ObjectProperty<Object> sample = new ObjectPropertyBase<Object>() {
>>> @Override
>>> public Object getBean() {
>>> return SimpleSubclassBean.this;
>>> }
>>>
>>> public String getName() {
>>> return "sample";
>>> }
>>>
>>> public Object getValue() {
>>> return null;
>>> }
>>>
>>> protected void invalidated() {
>>> i.incrementAndGet();
>>> }
>>> };
>>>
>>> public void invalidate() {
>>> sample.setValue(new Object());
>>> }
>>> }
>>>
>>> }
>>>
>>
>>
>> On 21.03.14 23:26, Kevin Rushforth wrote:
>>
>>> It does seem promising. We'll also need data to show the trade-offs to
>>> help inform whether it is worth making such a massive change.
>>>
>>> -- Kevin
>>>
>>>
>>> Stephen F Northover wrote:
>>>
>>>> This looks good. I wonder if we should make this (massive) change
>>>> before we lambda graphics and controls? Probably doesn't matter.
>>>> We'll need a JIRA and someone assigned to it in order to track the work.
>>>>
>>>> Steve
>>>>
>>>> On 2014-03-21 12:53 PM, Tom Schindl wrote:
>>>>
>>>>> Hi Richard,
>>>>>
>>>>> Coming back to this old thread and now that we are using lamdas all over
>>>>> I guess we could take one more look into that.
>>>>>
>>>>> I've prototyped an initial version by introducing a new internal type
>>>>> named InvalidatedSimpleObjectProperty (not the best name ever!) - see
>>>>> code pasted below.
>>>>>
>>>>> And now one can write code like this:
>>>>>
>>>>>
>>>>>> public final ObjectProperty<Rectangle2D> viewportProperty() {
>>>>>> if (viewport == null) {
>>>>>> viewport = new InvalidatedSimpleObjectProperty<>(this,
>>>>>> "viewport", (o) -> {
>>>>>> invalidateWidthHeight();
>>>>>> impl_markDirty(DirtyBits.NODE_VIEWPORT);
>>>>>> impl_geomChanged();
>>>>>> } );
>>>>>> }
>>>>>> return viewport;
>>>>>> }
>>>>>>
>>>>> instead of
>>>>>
>>>>>
>>>>>> public final ObjectProperty<Rectangle2D> viewportProperty() {
>>>>>> if (viewport == null) {
>>>>>> viewport = new ObjectPropertyBase<Rectangle2D>() {
>>>>>>
>>>>>> @Override
>>>>>> protected void invalidated() {
>>>>>> invalidateWidthHeight();
>>>>>> impl_markDirty(DirtyBits.NODE_VIEWPORT);
>>>>>> impl_geomChanged();
>>>>>> }
>>>>>>
>>>>>> @Override
>>>>>> public Object getBean() {
>>>>>> return ImageView.this;
>>>>>> }
>>>>>>
>>>>>> @Override
>>>>>> public String getName() {
>>>>>> return "viewport";
>>>>>> }
>>>>>> };
>>>>>> }
>>>>>> return viewport;
>>>>>> }
>>>>>>
>>>>> Which allows us to get rid of most of the ObjectPropertyBase sublcasses.
>>>>>
>>>>> Tom
>>>>>
>>>>>
>>>>>
>>>>>> package com.sun.javafx.property;
>>>>>>
>>>>>> import java.util.function.Consumer;
>>>>>>
>>>>>> import javafx.beans.property.SimpleObjectProperty;
>>>>>>
>>>>>> public final class InvalidatedSimpleObjectProperty<T> extends
>>>>>> SimpleObjectProperty<T> {
>>>>>> private final Consumer<InvalidatedSimpleObjectProperty<T>>
>>>>>> invalidationConsumer;
>>>>>> /**
>>>>>> * The constructor of {@code ObjectProperty}
>>>>>> *
>>>>>> * @param initialValue
>>>>>> * the initial value of the wrapped value
>>>>>> * @param invalidationConsumer
>>>>>> * the consumer to be called when the bean is
>>>>>> invalidated
>>>>>> */
>>>>>> public InvalidatedSimpleObjectProperty(T initialValue, final
>>>>>> Consumer<InvalidatedSimpleObjectProperty<T>> invalidationConsumer) {
>>>>>> super(initialValue);
>>>>>> if( invalidationConsumer == null ) {
>>>>>> throw new IllegalArgumentException("Consumer can not be
>>>>>> null");
>>>>>> }
>>>>>> this.invalidationConsumer = invalidationConsumer;
>>>>>> }
>>>>>>
>>>>>> /**
>>>>>> * The constructor of {@code ObjectProperty}
>>>>>> *
>>>>>> * @param bean
>>>>>> * the bean of this {@code ObjectProperty}
>>>>>> * @param name
>>>>>> * the name of this {@code ObjectProperty}
>>>>>> * @param invalidationConsumer
>>>>>> * the consumer to be called when the bean is
>>>>>> invalidated
>>>>>> */
>>>>>> public InvalidatedSimpleObjectProperty(Object bean, String
>>>>>> name, final Consumer<InvalidatedSimpleObjectProperty<T>>
>>>>>> invalidationConsumer) {
>>>>>> super(bean, name);
>>>>>> if( invalidationConsumer == null ) {
>>>>>> throw new IllegalArgumentException("Consumer can not be
>>>>>> null");
>>>>>> }
>>>>>> this.invalidationConsumer = invalidationConsumer;
>>>>>> }
>>>>>>
>>>>>> /**
>>>>>> * The constructor of {@code ObjectProperty}
>>>>>> *
>>>>>> * @param bean
>>>>>> * the bean of this {@code ObjectProperty}
>>>>>> * @param name
>>>>>> * the name of this {@code ObjectProperty}
>>>>>> * @param initialValue
>>>>>> * the initial value of the wrapped value
>>>>>> * @param invalidationConsumer
>>>>>> * the consumer to be called when the bean is
>>>>>> invalidated
>>>>>> */
>>>>>> public InvalidatedSimpleObjectProperty(Object bean, String
>>>>>> name, T initialValue, final
>>>>>> Consumer<InvalidatedSimpleObjectProperty<T>> invalidationConsumer) {
>>>>>> super(bean,name,initialValue);
>>>>>> if( invalidationConsumer == null ) {
>>>>>> throw new IllegalArgumentException("Consumer can not be
>>>>>> null");
>>>>>> }
>>>>>> this.invalidationConsumer = invalidationConsumer;
>>>>>> }
>>>>>> @Override
>>>>>> protected void invalidated() {
>>>>>> invalidationConsumer.accept(this);
>>>>>> }
>>>>>> }
>>>>>>
>>>>> On 22.01.13 10:30, Richard Bair wrote:
>>>>>
>>>>>>> Is the Java8 plan still there if not should the current
>>>>>>> Simple*Property
>>>>>>> subclasses who overload invalidated be changed to PropertyBase?
>>>>>>>
>>>>>> It is unlikely that we'll be able to do anything major here in Java
>>>>>> 8 just because we don't really have Lambda yet that we can play
>>>>>> with, and changing over every property is a big job. Unless we knew
>>>>>> it would be a major win. I would say, if you encounter a Simple*
>>>>>> property that has been subclassed, then we should fix it up as we go
>>>>>> to be a PropertyBase* guy instead.
>>>>>>
>>>>>>
>>
>>
More information about the openjfx-dev
mailing list