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.
>>>>>>>
>>>>>>>           
>>>
>>>   
> 

Reply via email to