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.


Reply via email to