Arghhh time for bed: Number is 179 vs 150 but I only ran it once so the numbers might be completely bogus!
Tom On 24.03.14 23:40, Tom Schindl wrote: > 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. >>>>>>>> >>>>>>>> >>>> >>>> >> >