*PropertyBase vs Simple*Property
Kevin Rushforth
kevin.rushforth at oracle.com
Mon Mar 24 22:31:42 UTC 2014
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.
>>>>>
>>>>>
>
>
More information about the openjfx-dev
mailing list