Improve codec performance
authorRobert Varga <rovarga@cisco.com>
Thu, 22 Jan 2015 20:18:22 +0000 (21:18 +0100)
committerRobert Varga <rovarga@cisco.com>
Tue, 27 Jan 2015 12:04:42 +0000 (13:04 +0100)
Convert the codecs from reflection to invocation API. Invoking
constructors and getters via reflection seems to be causing around
2% overhead which can be avoided if the call site is properly bound.

Change-Id: I0c303773a21913d9d1a6de952d37eb0dd022cbd1
Signed-off-by: Robert Varga <rovarga@cisco.com>
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/EncapsulatedValueCodec.java
code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/IdentifiableItemCodec.java
code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/UnionValueOptionContext.java
code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/ValueContext.java

index f3ebb96489ee62303290e7c06972b778ab7bcfa2..e5d58cc82f3cd15e60fcae946e588d9e20bcd47b 100644 (file)
@@ -9,11 +9,14 @@ package org.opendaylight.yangtools.binding.data.codec.impl;
 
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSortedMap;
-import java.lang.reflect.Constructor;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
 import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.util.Collection;
@@ -48,7 +51,9 @@ 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 Lookup LOOKUP = MethodHandles.publicLookup();
+    private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class, InvocationHandler.class);
+    private static final MethodType DATAOBJECT_TYPE = MethodType.methodType(DataObject.class, InvocationHandler.class);
     private static final Comparator<Method> METHOD_BY_ALPHABET = new Comparator<Method>() {
         @Override
         public int compare(final Method o1, final Method o2) {
@@ -61,7 +66,7 @@ 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;
-    private final Constructor<?> proxyConstructor;
+    private final MethodHandle proxyConstructor;
 
     // FIXME: this field seems to be unused
     private final Method augmentationGetter;
@@ -123,9 +128,9 @@ abstract class DataObjectCodecContext<T extends DataNodeContainer> extends DataC
 
         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");
+            proxyConstructor = LOOKUP.findConstructor(proxyClass, CONSTRUCTOR_TYPE).asType(DATAOBJECT_TYPE);
+        } catch (NoSuchMethodException | IllegalAccessException e) {
+            throw new IllegalStateException("Failed to find contructor for class " + proxyClass);
         }
     }
 
@@ -271,9 +276,9 @@ abstract class DataObjectCodecContext<T extends DataNodeContainer> extends DataC
 
     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);
+            return (DataObject) proxyConstructor.invokeExact((InvocationHandler)new LazyDataObject(this, node));
+        } catch (Throwable e) {
+            throw Throwables.propagate(e);
         }
     }
 
index 0b3b4678ad407aa4604bbb8a064f7ac667c6a093..3de5085c1d7c9f8decb1b407e91453781f6957a0 100644 (file)
@@ -7,8 +7,12 @@
  */
 package org.opendaylight.yangtools.binding.data.codec.impl;
 
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
 import java.lang.reflect.Method;
 import java.util.concurrent.Callable;
 import org.opendaylight.yangtools.binding.data.codec.impl.ValueTypeCodec.SchemaUnawareCodec;
@@ -19,26 +23,26 @@ import org.opendaylight.yangtools.binding.data.codec.impl.ValueTypeCodec.SchemaU
  * types, which are same as in NormalizedNode model.
  *
  */
-class EncapsulatedValueCodec extends ReflectionBasedCodec implements SchemaUnawareCodec {
+final class EncapsulatedValueCodec extends ReflectionBasedCodec implements SchemaUnawareCodec {
+    private static final Lookup LOOKUP = MethodHandles.publicLookup();
+    private static final MethodType OBJ_METHOD = MethodType.methodType(Object.class, Object.class);
+    private final MethodHandle constructor;
+    private final MethodHandle getter;
 
-    private final Method getter;
-    private final Constructor<?> constructor;
-
-    EncapsulatedValueCodec(final Class<?> typeClz) {
+    private EncapsulatedValueCodec(final Class<?> typeClz, final MethodHandle constructor, final MethodHandle getter) {
         super(typeClz);
-        try {
-            this.getter = typeClz.getMethod("getValue");
-            this.constructor = typeClz.getConstructor(getter.getReturnType());
-        } catch (NoSuchMethodException | SecurityException e) {
-            throw new IllegalStateException("Could not resolve required method.", e);
-        }
+        this.constructor = Preconditions.checkNotNull(constructor);
+        this.getter = Preconditions.checkNotNull(getter);
     }
 
     static Callable<EncapsulatedValueCodec> loader(final Class<?> typeClz) {
         return new Callable<EncapsulatedValueCodec>() {
             @Override
             public EncapsulatedValueCodec call() throws Exception {
-                return new EncapsulatedValueCodec(typeClz);
+                final Method m = typeClz.getMethod("getValue");
+                final MethodHandle getter = LOOKUP.unreflect(m).asType(OBJ_METHOD);
+                final MethodHandle constructor = LOOKUP.findConstructor(typeClz, MethodType.methodType(void.class, m.getReturnType())).asType(OBJ_METHOD);
+                return new EncapsulatedValueCodec(typeClz, constructor, getter);
             }
         };
     }
@@ -46,18 +50,18 @@ class EncapsulatedValueCodec extends ReflectionBasedCodec implements SchemaUnawa
     @Override
     public Object deserialize(final Object input) {
         try {
-            return constructor.newInstance(input);
-        } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
-            throw new IllegalStateException(e);
+            return constructor.invokeExact(input);
+        } catch (Throwable e) {
+            throw Throwables.propagate(e);
         }
     }
 
     @Override
     public Object serialize(final Object input) {
         try {
-            return getter.invoke(input);
-        } catch (IllegalAccessException | InvocationTargetException e) {
-            throw new IllegalStateException(e);
+            return getter.invokeExact(input);
+        } catch (Throwable e) {
+            throw Throwables.propagate(e);
         }
     }
 }
\ No newline at end of file
index ebaaf7ab4bd07e6ff9034bea44ddddcc6ca20aa6..e22f1f88fa7d2c0864d9d7ca73710be839f4077e 100644 (file)
@@ -7,9 +7,12 @@
  */
 package org.opendaylight.yangtools.binding.data.codec.impl;
 
+import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableMap;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
 import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.LinkedHashMap;
@@ -23,16 +26,25 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdent
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 
 final class IdentifiableItemCodec implements Codec<NodeIdentifierWithPredicates, IdentifiableItem<?, ?>> {
+    private static final Lookup LOOKUP = MethodHandles.publicLookup();
+    private final MethodHandle ctor;
+    private final MethodHandle ctorInvoker;
     private final Map<QName, ValueContext> keyValueContexts;
     private final ListSchemaNode schema;
-    private final Constructor<? extends Identifier<?>> constructor;
     private final Class<?> identifiable;
 
     public IdentifiableItemCodec(final ListSchemaNode schema, final Class<? extends Identifier<?>> keyClass,
             final Class<?> identifiable, final Map<QName, ValueContext> keyValueContexts) {
         this.schema = schema;
         this.identifiable = identifiable;
-        this.constructor = getConstructor(keyClass);
+
+        try {
+            ctor = LOOKUP.unreflectConstructor(getConstructor(keyClass));
+        } catch (IllegalAccessException e) {
+            throw new IllegalArgumentException("Missing construct in class " + keyClass);
+        }
+        final MethodHandle inv = MethodHandles.spreadInvoker(ctor.type(), 0);
+        this.ctorInvoker = inv.asType(inv.type().changeReturnType(Identifier.class));
 
         /*
          * We need to re-index to make sure we instantiate nodes in the order in which
@@ -56,9 +68,9 @@ final class IdentifiableItemCodec implements Codec<NodeIdentifierWithPredicates,
 
         final Identifier<?> identifier;
         try {
-            identifier = constructor.newInstance(bindingValues.toArray());
-        } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
-            throw new IllegalStateException(String.format("Failed to instantiate key class %s", constructor.getDeclaringClass()), e);
+            identifier = (Identifier<?>) ctorInvoker.invokeExact(ctor, bindingValues.toArray());
+        } catch (Throwable e) {
+            throw Throwables.propagate(e);
         }
 
         @SuppressWarnings({ "rawtypes", "unchecked" })
index f1cb431f58cd8314129336d05f5a361258e695fe..7c4c14453f8649ae8b55fe8002a656d567de9d2e 100644 (file)
@@ -7,35 +7,48 @@
  */
 package org.opendaylight.yangtools.binding.data.codec.impl;
 
-import java.lang.reflect.InvocationTargetException;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
 import java.lang.reflect.Method;
 import org.opendaylight.yangtools.concepts.Codec;
 
 final class UnionValueOptionContext {
+    private static final Lookup LOOKUP = MethodHandles.publicLookup();
+    private static final MethodType OBJECT_TYPE = MethodType.methodType(Object.class, Object.class);
+    private final Class<?> bindingType;
+    // FIXME: migrate to invocation
+    private final MethodHandle getter;
+    private final Codec<Object,Object> codec;
 
-    final Method getter;
-    final Class<?> bindingType;
-    final Codec<Object,Object> codec;
+    UnionValueOptionContext(final Class<?> valueType, final Method getter, final Codec<Object, Object> codec) {
+        this.bindingType = Preconditions.checkNotNull(valueType);
+        this.codec = Preconditions.checkNotNull(codec);
 
-    UnionValueOptionContext(final Class<?> valueType,final Method getter, final Codec<Object, Object> codec) {
-        this.getter = getter;
-        this.bindingType = valueType;
-        this.codec = codec;
+        try {
+            this.getter = LOOKUP.unreflect(getter).asType(OBJECT_TYPE);
+        } catch (IllegalAccessException e) {
+            throw new IllegalStateException("Failed to access method " + getter, e);
+        }
     }
 
-    public Object serialize(final Object input) {
-        Object baValue = getValueFrom(input);
-        if(baValue != null) {
-            return codec.serialize(baValue);
+    Object serialize(final Object input) {
+        final Object baValue = getValueFrom(input);
+        if (baValue == null) {
+            return null;
         }
-        return null;
+
+        return codec.serialize(baValue);
     }
 
-    public Object getValueFrom(final Object input) {
+    Object getValueFrom(final Object input) {
         try {
-            return getter.invoke(input);
-        } catch (IllegalAccessException  | InvocationTargetException e) {
-            throw new IllegalStateException(e);
+            return getter.invokeExact(input);
+        } catch (Throwable e) {
+            throw Throwables.propagate(e);
         }
     }
 
@@ -49,13 +62,11 @@ final class UnionValueOptionContext {
         if (this == obj) {
             return true;
         }
-        if (obj == null) {
+        if (!(obj instanceof UnionValueOptionContext)) {
             return false;
         }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        UnionValueOptionContext other = (UnionValueOptionContext) obj;
+
+        final UnionValueOptionContext other = (UnionValueOptionContext) obj;
         return bindingType.equals(other.bindingType);
     }
 }
\ No newline at end of file
index 86c0057031c1af45a302af9c06d4051c9a518450..1308846cc8ebb79351382e93cf51e14850c704f9 100644 (file)
@@ -8,36 +8,45 @@
 package org.opendaylight.yangtools.binding.data.codec.impl;
 
 import com.google.common.base.Preconditions;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
+import com.google.common.base.Throwables;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
 import org.opendaylight.yangtools.concepts.Codec;
 import org.opendaylight.yangtools.yang.binding.BindingMapping;
 
 final class ValueContext {
+    private static final Lookup LOOKUP = MethodHandles.publicLookup();
+    private static final MethodType OBJECT_METHOD = MethodType.methodType(Object.class, Object.class);
     private final Codec<Object, Object> codec;
-    private final Method getter;
+    private final MethodHandle getter;
+    private final Class<?> identifier;
+    private final String getterName;
 
     ValueContext(final Class<?> identifier, final LeafNodeCodecContext leaf) {
-        final String getterName = BindingCodecContext.GETTER_PREFIX
-                + BindingMapping.getClassName(leaf.getDomPathArgument().getNodeType());
+        getterName = BindingCodecContext.GETTER_PREFIX + BindingMapping.getClassName(leaf.getDomPathArgument().getNodeType());
         try {
-            getter = identifier.getMethod(getterName);
-        } catch (NoSuchMethodException | SecurityException e) {
-            throw new IllegalStateException(e);
+            getter = LOOKUP.unreflect(identifier.getMethod(getterName)).asType(OBJECT_METHOD);
+        } catch (IllegalAccessException | NoSuchMethodException | SecurityException e) {
+            throw new IllegalStateException(String.format("Cannot find method %s in class %s", getterName, identifier), e);
         }
+        this.identifier = identifier;
         codec = leaf.getValueCodec();
     }
 
     Object getAndSerialize(final Object obj) {
+        final Object value;
         try {
-            final Object value = getter.invoke(obj);
-            Preconditions.checkArgument(value != null,
-                    "All keys must be specified for %s. Missing key is %s. Supplied key is %s",
-                    getter.getDeclaringClass(), getter.getName(), obj);
-            return codec.serialize(value);
-        } catch (IllegalAccessException | InvocationTargetException e) {
-            throw new IllegalArgumentException(e);
+            value = getter.invokeExact(obj);
+        } catch (Throwable e) {
+            throw Throwables.propagate(e);
         }
+
+        Preconditions.checkArgument(value != null,
+                "All keys must be specified for %s. Missing key is %s. Supplied key is %s",
+                identifier, getterName, obj);
+        return codec.serialize(value);
     }
 
     Object deserialize(final Object obj) {