*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