Cleanup binding proxy instantiation 57/13757/1
authorRobert Varga <rovarga@cisco.com>
Fri, 19 Dec 2014 14:04:34 +0000 (15:04 +0100)
committerRobert Varga <rovarga@cisco.com>
Fri, 19 Dec 2014 14:30:01 +0000 (15:30 +0100)
Profiling of BGP topology provider has revealed a performance bottleneck
when instantiating proxies for list children. As it turns out around 68%
of CPU cycles is being burned in looking up the proxy class and its
contructor. This patch performs the obvious tradeoff by caching it and
exposing a utility function to subclasses.

Change-Id: Icb76f232df8223925fa802859313bbf63ee58eeb
Signed-off-by: Robert Varga <rovarga@cisco.com>
code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/AugmentationNodeContext.java
code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/CaseNodeCodecContext.java
code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/ContainerNodeCodecContext.java
code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/DataObjectCodecContext.java
code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/LazyDataObject.java
code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/ListNodeCodecContext.java

index e3abf70b31c77b820afda74d99fdf927753082e0..feb731f0b97f6ae7a5483d92513f363bb520109a 100644 (file)
@@ -8,7 +8,6 @@
 package org.opendaylight.yangtools.binding.data.codec.impl;
 
 import com.google.common.base.Preconditions;
-
 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
@@ -22,6 +21,6 @@ final class AugmentationNodeContext extends DataObjectCodecContext<AugmentationS
     @Override
     protected Object dataFromNormalizedNode(final NormalizedNode<?, ?> normalizedNode) {
         Preconditions.checkArgument(normalizedNode instanceof AugmentationNode);
-        return LazyDataObject.create(this, (AugmentationNode) normalizedNode);
+        return createBindingProxy((AugmentationNode)normalizedNode);
     }
 }
\ No newline at end of file
index 5f41e8df78ec5a7f869df059ca6575c8bad31ebd..86d8098fc2b769cc7e3ecd583cf9625733b09c71 100644 (file)
@@ -8,9 +8,7 @@
 package org.opendaylight.yangtools.binding.data.codec.impl;
 
 import com.google.common.base.Preconditions;
-
 import java.util.List;
-
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
@@ -30,7 +28,6 @@ final class CaseNodeCodecContext extends DataObjectCodecContext<ChoiceCaseNode>
     @Override
     protected Object dataFromNormalizedNode(final NormalizedNode<?, ?> normalizedNode) {
         Preconditions.checkState(normalizedNode instanceof ChoiceNode);
-        return LazyDataObject.create(this, (ChoiceNode) normalizedNode);
+        return createBindingProxy((ChoiceNode) normalizedNode);
     }
-
 }
\ No newline at end of file
index 8eb335230904de2c075189189ec7056ec32ac274..f70e7ce996818985b57ed74cfad405de87d7d95a 100644 (file)
@@ -8,7 +8,6 @@
 package org.opendaylight.yangtools.binding.data.codec.impl;
 
 import com.google.common.base.Preconditions;
-
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
@@ -22,7 +21,7 @@ final class ContainerNodeCodecContext extends DataObjectCodecContext<ContainerSc
     @Override
     protected Object dataFromNormalizedNode(final NormalizedNode<?, ?> data) {
         Preconditions.checkState(data instanceof ContainerNode);
-        return LazyDataObject.create(this, (ContainerNode) data);
+        return createBindingProxy((ContainerNode) data);
     }
 
 }
\ No newline at end of file
index 5725e186ba0be23d12bf88251e4fc6e79e5daa8d..f3ebb96489ee62303290e7c06972b778ab7bcfa2 100644 (file)
@@ -11,7 +11,11 @@ import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSortedMap;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
 import java.util.Collection;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -44,9 +48,8 @@ import org.slf4j.LoggerFactory;
 
 abstract class DataObjectCodecContext<T extends DataNodeContainer> extends DataContainerCodecContext<T> {
     private static final Logger LOG = LoggerFactory.getLogger(DataObjectCodecContext.class);
-
+    private static final Class<?>[] CONSTRUCTOR_ARGS = new Class[] { InvocationHandler.class };
     private static final Comparator<Method> METHOD_BY_ALPHABET = new Comparator<Method>() {
-
         @Override
         public int compare(final Method o1, final Method o2) {
             return o1.getName().compareTo(o2.getName());
@@ -58,7 +61,10 @@ abstract class DataObjectCodecContext<T extends DataNodeContainer> extends DataC
     private final ImmutableSortedMap<Method, NodeContextSupplier> byMethod;
     private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byStreamClass;
     private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byBindingArgClass;
-    protected final Method augmentationGetter;
+    private final Constructor<?> proxyConstructor;
+
+    // FIXME: this field seems to be unused
+    private final Method augmentationGetter;
 
     protected DataObjectCodecContext(final DataContainerCodecPrototype<T> prototype) {
         super(prototype);
@@ -114,6 +120,13 @@ abstract class DataObjectCodecContext<T extends DataNodeContainer> extends DataC
         this.byStreamClass = ImmutableMap.copyOf(byStreamClassBuilder);
         byBindingArgClassBuilder.putAll(byStreamClass);
         this.byBindingArgClass = ImmutableMap.copyOf(byBindingArgClassBuilder);
+
+        final Class<?> proxyClass = Proxy.getProxyClass(bindingClass().getClassLoader(),  new Class[] { bindingClass() });
+        try {
+            proxyConstructor = proxyClass.getConstructor(CONSTRUCTOR_ARGS);
+        } catch (NoSuchMethodException | SecurityException e) {
+            throw new IllegalStateException("Failed to find constructor");
+        }
     }
 
     @Override
@@ -256,6 +269,14 @@ abstract class DataObjectCodecContext<T extends DataNodeContainer> extends DataC
         return null;
     }
 
+    protected final DataObject createBindingProxy(final NormalizedNodeContainer<?, ?, ?> node) {
+        try {
+            return (DataObject) proxyConstructor.newInstance(new Object[] { new LazyDataObject(this, node) });
+        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            throw new IllegalStateException("Failed to construct proxy for " + node, e);
+        }
+    }
+
     public Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAllAugmentationsFrom(
             final NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> data) {
 
index 6a22641e1353c8e6c5b19dc4020d64731aa4956d..f1227ed997afaa35bd992812c300f67aa8d8a260 100644 (file)
@@ -21,7 +21,6 @@ import java.util.concurrent.ConcurrentHashMap;
 import org.opendaylight.yangtools.binding.data.codec.util.AugmentationReader;
 import org.opendaylight.yangtools.yang.binding.Augmentable;
 import org.opendaylight.yangtools.yang.binding.Augmentation;
-import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
@@ -46,18 +45,11 @@ class LazyDataObject implements InvocationHandler, AugmentationReader {
     private volatile Integer cachedHashcode = null;
 
     @SuppressWarnings({ "rawtypes", "unchecked" })
-    private LazyDataObject(final DataObjectCodecContext<?> ctx, final NormalizedNodeContainer data) {
+    LazyDataObject(final DataObjectCodecContext<?> ctx, final NormalizedNodeContainer data) {
         this.context = Preconditions.checkNotNull(ctx, "Context must not be null");
         this.data = Preconditions.checkNotNull(data, "Data must not be null");
     }
 
-    @SuppressWarnings("rawtypes")
-    static DataObject create(final DataObjectCodecContext ctx, final NormalizedNodeContainer<?, ?, ?> data) {
-        Class<?> bindingClass = ctx.bindingClass();
-        return (DataObject) Proxy.newProxyInstance(bindingClass.getClassLoader(), new Class<?>[] { bindingClass },
-                new LazyDataObject(ctx, data));
-    }
-
     @Override
     public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
         if (method.getParameterTypes().length == 0) {
@@ -222,5 +214,4 @@ class LazyDataObject implements InvocationHandler, AugmentationReader {
         }
         return true;
     }
-
 }
index ca2c94dadf06067c901647107b5671469e93b012..da174b0ab687a48ef397bb9b61ee66951e16e6e3 100644 (file)
@@ -8,11 +8,9 @@
 package org.opendaylight.yangtools.binding.data.codec.impl;
 
 import com.google.common.collect.Iterables;
-
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.List;
-
 import org.opendaylight.yangtools.concepts.Codec;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.Identifiable;
@@ -30,7 +28,6 @@ import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 
 final class ListNodeCodecContext extends DataObjectCodecContext<ListSchemaNode> {
-
     private final Codec<NodeIdentifierWithPredicates, IdentifiableItem<?, ?>> codec;
     private final Method keyGetter;
 
@@ -87,14 +84,15 @@ final class ListNodeCodecContext extends DataObjectCodecContext<ListSchemaNode>
     protected Object dataFromNormalizedNode(final NormalizedNode<?, ?> node) {
         if (node instanceof MapNode) {
             return fromMap((MapNode) node);
-        } else if(node instanceof MapEntryNode)  {
+        } else if (node instanceof MapEntryNode) {
             return fromMapEntry((MapEntryNode) node);
         } else if (node instanceof UnkeyedListNode) {
             return fromUnkeyedList((UnkeyedListNode) node);
-        }  else if(node instanceof UnkeyedListEntryNode) {
+        } else if (node instanceof UnkeyedListEntryNode) {
             return fromUnkeyedListEntry((UnkeyedListEntryNode) node);
+        } else {
+            throw new IllegalStateException("Unsupported data type " + node.getClass());
         }
-        throw new IllegalStateException("Unsupported data type " + node.getClass());
     }
 
     private List<DataObject> fromMap(final MapNode nodes) {
@@ -106,11 +104,11 @@ final class ListNodeCodecContext extends DataObjectCodecContext<ListSchemaNode>
     }
 
     private DataObject fromMapEntry(final MapEntryNode node) {
-        return LazyDataObject.create(this, node);
+        return createBindingProxy(node);
     }
 
     private DataObject fromUnkeyedListEntry(final UnkeyedListEntryNode node) {
-        return LazyDataObject.create(this, node);
+        return createBindingProxy(node);
     }
 
     private List<DataObject> fromUnkeyedList(final UnkeyedListNode nodes) {
@@ -123,8 +121,9 @@ final class ListNodeCodecContext extends DataObjectCodecContext<ListSchemaNode>
     }
 
     @Override
+    @SuppressWarnings("rawtypes")
     Object getBindingChildValue(final Method method, final NormalizedNodeContainer dom) {
-        if (method.equals(keyGetter) && dom instanceof MapEntryNode) {
+        if (dom instanceof MapEntryNode && method.equals(keyGetter)) {
             NodeIdentifierWithPredicates identifier = ((MapEntryNode) dom).getIdentifier();
             return codec.deserialize(identifier).getKey();
         }