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