From 186e3bf4027a8c2b9bf0550f1aacff5ee4be2313 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Wed, 26 Sep 2012 16:40:18 +0200 Subject: bind + argc specialization = 20x perf boost Default logic of mirror construction, which gets triggered via reflectField/reflectMethod/reflectConstructor, validates a lot of facts about its arguments. This takes quite a bit of time, which significantly degrades performance of reflection-heavy applications. Proposed two changes provide an order of magnitude performance boost to a simple app, which repeatedly invokes the same method for different receiver instances. --- src/reflect/scala/reflect/api/Mirrors.scala | 10 +++++ .../scala/reflect/runtime/JavaMirrors.scala | 50 +++++++++++++++++++++- 2 files changed, 58 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/reflect/scala/reflect/api/Mirrors.scala b/src/reflect/scala/reflect/api/Mirrors.scala index d0d8a37584..d30563c706 100644 --- a/src/reflect/scala/reflect/api/Mirrors.scala +++ b/src/reflect/scala/reflect/api/Mirrors.scala @@ -352,6 +352,11 @@ trait Mirrors { self: Universe => * the value of the base field. To achieve overriding behavior, use reflectMethod on an accessor. */ def set(value: Any): Unit + + /** Creates a new mirror which uses the same symbol, but is bound to a different receiver. + * This is significantly faster than recreating the mirror from scratch. + */ + def bind(newReceiver: Any): FieldMirror } /** A mirror that reflects a method. @@ -373,6 +378,11 @@ trait Mirrors { self: Universe => * with invoking the corresponding method or constructor. */ def apply(args: Any*): Any + + /** Creates a new mirror which uses the same symbol, but is bound to a different receiver. + * This is significantly faster than recreating the mirror from scratch. + */ + def bind(newReceiver: Any): MethodMirror } /** A mirror that reflects the instance or static parts of a runtime class. diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 57cfb8b515..67b24cbdea 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -282,6 +282,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni if (!symbol.isMutable) ErrorSetImmutableField(symbol) jfield.set(receiver, value) } + def bind(newReceiver: Any) = new JavaFieldMirror(newReceiver, symbol) override def toString = s"field mirror for ${symbol.fullName} (bound to $receiver)" } @@ -329,7 +330,16 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private def mkJavaMethodMirror[T: ClassTag](receiver: T, symbol: MethodSymbol): JavaMethodMirror = { if (isBytecodelessMethod(symbol)) new JavaBytecodelessMethodMirror(receiver, symbol) else if (symbol.paramss.flatten exists (p => isByNameParamType(p.info))) new JavaByNameMethodMirror(receiver, symbol) - else new JavaVanillaMethodMirror(receiver, symbol) + else { + symbol.paramss.flatten.length match { + case 0 => new JavaVanillaMethodMirror0(receiver, symbol) + case 1 => new JavaVanillaMethodMirror1(receiver, symbol) + case 2 => new JavaVanillaMethodMirror2(receiver, symbol) + case 3 => new JavaVanillaMethodMirror3(receiver, symbol) + case 4 => new JavaVanillaMethodMirror4(receiver, symbol) + case _ => new JavaVanillaMethodMirror(receiver, symbol) + } + } } private abstract class JavaMethodMirror(val symbol: MethodSymbol) @@ -340,8 +350,10 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni jmeth } + def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver, args.asInstanceOf[Seq[AnyRef]]: _*) + def jinvoke(jmeth: jMethod, receiver: Any, args: Seq[Any]): Any = { - val result = jmeth.invoke(receiver, args.asInstanceOf[Seq[AnyRef]]: _*) + val result = jinvokeraw(jmeth, receiver, args) if (jmeth.getReturnType == java.lang.Void.TYPE) () else result } @@ -351,11 +363,43 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private class JavaVanillaMethodMirror(val receiver: Any, symbol: MethodSymbol) extends JavaMethodMirror(symbol) { + def bind(newReceiver: Any) = new JavaVanillaMethodMirror(newReceiver, symbol) def apply(args: Any*): Any = jinvoke(jmeth, receiver, args) } + private class JavaVanillaMethodMirror0(receiver: Any, symbol: MethodSymbol) + extends JavaVanillaMethodMirror(receiver, symbol) { + override def bind(newReceiver: Any) = new JavaVanillaMethodMirror0(newReceiver, symbol) + override def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver) + } + + private class JavaVanillaMethodMirror1(receiver: Any, symbol: MethodSymbol) + extends JavaVanillaMethodMirror(receiver, symbol) { + override def bind(newReceiver: Any) = new JavaVanillaMethodMirror1(newReceiver, symbol) + override def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef]) + } + + private class JavaVanillaMethodMirror2(receiver: Any, symbol: MethodSymbol) + extends JavaVanillaMethodMirror(receiver, symbol) { + override def bind(newReceiver: Any) = new JavaVanillaMethodMirror2(newReceiver, symbol) + override def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef]) + } + + private class JavaVanillaMethodMirror3(receiver: Any, symbol: MethodSymbol) + extends JavaVanillaMethodMirror(receiver, symbol) { + override def bind(newReceiver: Any) = new JavaVanillaMethodMirror3(newReceiver, symbol) + override def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef]) + } + + private class JavaVanillaMethodMirror4(receiver: Any, symbol: MethodSymbol) + extends JavaVanillaMethodMirror(receiver, symbol) { + override def bind(newReceiver: Any) = new JavaVanillaMethodMirror4(newReceiver, symbol) + override def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef], args(3).asInstanceOf[AnyRef]) + } + private class JavaByNameMethodMirror(val receiver: Any, symbol: MethodSymbol) extends JavaMethodMirror(symbol) { + def bind(newReceiver: Any) = new JavaByNameMethodMirror(newReceiver, symbol) def apply(args: Any*): Any = { val transformed = map2(args.toList, symbol.paramss.flatten)((arg, param) => if (isByNameParamType(param.info)) () => arg else arg) jinvoke(jmeth, receiver, transformed) @@ -364,6 +408,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private class JavaBytecodelessMethodMirror[T: ClassTag](val receiver: T, symbol: MethodSymbol) extends JavaMethodMirror(symbol) { + def bind(newReceiver: Any) = new JavaBytecodelessMethodMirror(newReceiver.asInstanceOf[T], symbol) def apply(args: Any*): Any = { // checking type conformance is too much of a hassle, so we don't do it here // actually it's not even necessary, because we manually dispatch arguments below @@ -420,6 +465,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private class JavaConstructorMirror(val outer: AnyRef, val symbol: MethodSymbol) extends MethodMirror { + def bind(newReceiver: Any) = new JavaConstructorMirror(newReceiver.asInstanceOf[AnyRef], symbol) override val receiver = outer lazy val jconstr = { val jconstr = constructorToJava(symbol) -- cgit v1.2.3