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