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