*PropertyBase vs Simple*Property

Tom Schindl tom.schindl at bestsolution.at
Mon Mar 24 22:36:03 UTC 2014


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



More information about the openjfx-dev mailing list