*PropertyBase vs Simple*Property
Tom Schindl
tom.schindl at bestsolution.at
Mon Mar 24 22:43:22 UTC 2014
Arghhh time for bed:
Number is 179 vs 150 but I only ran it once so the numbers might be
completely bogus!
Tom
On 24.03.14 23:40, Tom Schindl wrote:
> Ups there was an error in my test for the last Call line so the numbers
> there are:
>
> 38 (lambda) vs 32 (subclass)
>
>> 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();
>> TestObject o = target.get(0);
>> for( int i = 0; i < invokationOverheadCallCount; i++ ) {
>> o.invalidate();
>> }
>> 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 24.03.14 23:36, Tom Schindl wrote:
>> 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