*PropertyBase vs Simple*Property

Tom Schindl tom.schindl at bestsolution.at
Mon Mar 24 22:10:44 UTC 2014


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