diff options
author | Rich Hickey <richhickey@gmail.com> | 2010-04-19 15:07:13 -0400 |
---|---|---|
committer | Rich Hickey <richhickey@gmail.com> | 2010-04-19 15:07:13 -0400 |
commit | e660e467789ccc8e9922948b3498939e0239fc7c (patch) | |
tree | 2010ebf34798141234344912b4a3f624743d39ab | |
parent | ccd7ae47ece97bed6b5eb39e5ba8779b214548cc (diff) |
new perf for protocols
-rw-r--r-- | src/clj/clojure/core_deftype.clj | 43 | ||||
-rw-r--r-- | src/jvm/clojure/lang/Compiler.java | 72 | ||||
-rw-r--r-- | src/jvm/clojure/lang/MethodImplCache.java | 27 |
3 files changed, 62 insertions, 80 deletions
diff --git a/src/clj/clojure/core_deftype.clj b/src/clj/clojure/core_deftype.clj index 4409ea75..cf9da18f 100644 --- a/src/clj/clojure/core_deftype.clj +++ b/src/clj/clojure/core_deftype.clj @@ -357,14 +357,14 @@ ;;;;;;;;;;;;;;;;;;;;;;; protocols ;;;;;;;;;;;;;;;;;;;;;;;; (defn- expand-method-impl-cache [#^clojure.lang.MethodImplCache cache c f] - (let [cs (into {} (remove (fn [[c f]] (nil? f)) (map vec (partition 2 (.table cache))))) - cs (assoc cs c f) + (let [cs (into {} (remove (fn [[c e]] (nil? e)) (map vec (partition 2 (.table cache))))) + cs (assoc cs c (clojure.lang.MethodImplCache$Entry. c f)) [shift mask] (min-hash (keys cs)) table (make-array Object (* 2 (inc mask))) - table (reduce (fn [#^objects t [c f]] + table (reduce (fn [#^objects t [c e]] (let [i (* 2 (int (shift-mask shift mask (hash c))))] (aset t i c) - (aset t (inc i) f) + (aset t (inc i) e) t)) table cs)] (clojure.lang.MethodImplCache. (.protocol cache) (.methodk cache) shift mask table))) @@ -412,9 +412,11 @@ [protocol x] (boolean (find-protocol-impl protocol x))) -(defn -cache-protocol-fn [#^clojure.lang.AFunction pf x] +(defn -cache-protocol-fn [#^clojure.lang.AFunction pf x #^Class c #^clojure.lang.IFn interf] (let [cache (.__methodImplCache pf) - f (find-protocol-method (.protocol cache) (.methodk cache) x)] + f (if (.isInstance c x) + interf + (find-protocol-method (.protocol cache) (.methodk cache) x))] (when-not f (throw (IllegalArgumentException. (str "No implementation of method: " (.methodk cache) " of protocol: " (:var (.protocol cache)) @@ -424,25 +426,30 @@ (defn- emit-method-builder [on-interface method on-method arglists] (let [methodk (keyword method) - gthis (with-meta (gensym) {:tag 'clojure.lang.AFunction})] + gthis (with-meta (gensym) {:tag 'clojure.lang.AFunction}) + ginterf (gensym)] `(fn [cache#] - (let [#^clojure.lang.AFunction f# + (let [~ginterf + (fn + ~@(map + (fn [args] + (let [gargs (map #(gensym (str "gf__" % "__")) args) + target (first gargs)] + `([~@gargs] + (. ~(with-meta target {:tag on-interface}) ~(or on-method method) ~@(rest gargs))))) + arglists)) + #^clojure.lang.AFunction f# (fn ~gthis ~@(map (fn [args] (let [gargs (map #(gensym (str "gf__" % "__")) args) target (first gargs)] `([~@gargs] - (~@(if on-interface - `(if (instance? ~on-interface ~target) - (. ~(with-meta target {:tag on-interface}) ~(or on-method method) ~@(rest gargs))) - `(do)) - (let [cache# (.__methodImplCache ~gthis)] - ;(assert cache#) - (let [f# (or (.fnFor cache# (clojure.lang.Util/classOf ~target)) - (-cache-protocol-fn ~gthis ~target))] - ;(assert f#) - (f# ~@gargs))))))) + (let [cache# (.__methodImplCache ~gthis) + f# (.fnFor cache# (clojure.lang.Util/classOf ~target))] + (if f# + (f# ~@gargs) + ((-cache-protocol-fn ~gthis ~target ~on-interface ~ginterf) ~@gargs)))))) arglists))] (set! (.__methodImplCache f#) cache#) f#)))) diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index 36594ce1..3febaee6 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -2885,11 +2885,8 @@ static class InvokeExpr implements Expr{ } public void emitProto(C context, ObjExpr objx, GeneratorAdapter gen){ - Label elseLabel = gen.newLabel(); - Label notSameClassLabel = gen.newLabel(); - Label faultLabel = gen.newLabel(); - Label callLabel = gen.newLabel(); Label onLabel = gen.newLabel(); + Label callLabel = gen.newLabel(); Label endLabel = gen.newLabel(); Var v = ((VarExpr)fexpr).var; @@ -2897,70 +2894,26 @@ static class InvokeExpr implements Expr{ Expr e = (Expr) args.nth(0); e.emit(C.EXPRESSION, objx, gen); gen.dup(); //target, target + gen.invokeStatic(UTIL_TYPE,Method.getMethod("Class classOf(Object)")); //target,class + gen.loadThis(); + gen.getField(objx.objtype, objx.cachedClassName(siteIndex),CLASS_TYPE); //target,class,cached-class + gen.visitJumpInsn(IF_ACMPEQ, callLabel); //target if(protocolOn != null) { + gen.dup(); //target, target gen.instanceOf(Type.getType(protocolOn)); gen.ifZCmp(GeneratorAdapter.NE, onLabel); - gen.dup(); } - gen.invokeStatic(UTIL_TYPE,Method.getMethod("Class classOf(Object)")); //target,class - gen.dup(); //target,class,class - gen.loadThis(); - gen.getField(objx.objtype, objx.cachedClassName(siteIndex),CLASS_TYPE); //target,class,class,cached-class - gen.visitJumpInsn(IF_ACMPNE, notSameClassLabel); //target,class - objx.emitVar(gen, v); - gen.invokeVirtual(VAR_TYPE, Method.getMethod("Object getRawRoot()")); //target, class, proto-fn - gen.dup(); //target, class, proto-fn, proto-fn - gen.loadThis(); - gen.getField(objx.objtype, objx.cachedProtoFnName(siteIndex),AFUNCTION_TYPE); //target,class, proto-fn,proto-fn,cached-proto-fn - gen.visitJumpInsn(IF_ACMPNE, elseLabel); //target,class, proto-fn - gen.pop(); //target,class - gen.pop(); //target - gen.loadThis(); - gen.getField(objx.objtype, objx.cachedProtoImplName(siteIndex),IFN_TYPE); //target,proto-impl - gen.swap(); //proto-impl, target - gen.goTo(callLabel); - - gen.mark(notSameClassLabel); //target,class - gen.dup(); //target,class,class + gen.mark(callLabel); //target + gen.dup(); //target, target + gen.invokeStatic(UTIL_TYPE,Method.getMethod("Class classOf(Object)")); //target,class gen.loadThis(); gen.swap(); - gen.putField(objx.objtype, objx.cachedClassName(siteIndex),CLASS_TYPE); //target,class + gen.putField(objx.objtype, objx.cachedClassName(siteIndex),CLASS_TYPE); //target objx.emitVar(gen, v); - gen.invokeVirtual(VAR_TYPE, Method.getMethod("Object getRawRoot()")); //target, class, proto-fn - - gen.mark(elseLabel); //target, class, proto-fn - gen.checkCast(AFUNCTION_TYPE); - gen.dup(); //target,class,proto-fn,proto-fn - gen.loadThis(); - gen.swap(); - gen.putField(objx.objtype, objx.cachedProtoFnName(siteIndex),AFUNCTION_TYPE); //target, class, proto-fn - gen.dupX1(); //target, proto-fn, class, proto-fn - gen.getField(AFUNCTION_TYPE,"__methodImplCache", Type.getType(MethodImplCache.class)); //target,protofn,class,cache - gen.swap(); //target,protofn,cache,class - gen.invokeVirtual(Type.getType(MethodImplCache.class),Method.getMethod("clojure.lang.IFn fnFor(Class)")); //target,protofn,impl - gen.dup(); //target,protofn,impl, impl - gen.ifNull(faultLabel); //target,protofn,impl - gen.swap(); //target,impl, protofn - gen.pop(); //target, impl - gen.dup(); //target,impl, impl - gen.loadThis(); + gen.invokeVirtual(VAR_TYPE, Method.getMethod("Object getRawRoot()")); //target, proto-fn gen.swap(); - gen.putField(objx.objtype, objx.cachedProtoImplName(siteIndex),IFN_TYPE); //target,impl - gen.swap(); //impl,target - gen.goTo(callLabel); - - //not in fn table, null out cached fn and use proto-fn itself (which should seed table for next time) - gen.mark(faultLabel); //target,protofn,null - gen.pop(); //target, protofn - gen.swap(); //protofn, target - gen.loadThis(); - gen.visitInsn(Opcodes.ACONST_NULL); - gen.putField(objx.objtype, objx.cachedProtoFnName(siteIndex), AFUNCTION_TYPE); //target, class, proto-fn - gen.goTo(callLabel); - - gen.mark(callLabel); //impl, target emitArgsAndCall(1, context,objx,gen); gen.goTo(endLabel); @@ -2976,9 +2929,8 @@ static class InvokeExpr implements Expr{ Method m = new Method(onMethod.getName(), Type.getReturnType(onMethod), Type.getArgumentTypes(onMethod)); gen.invokeInterface(Type.getType(protocolOn), m); HostExpr.emitBoxReturn(objx, gen, onMethod.getReturnType()); - } + } gen.mark(endLabel); - } void emitArgsAndCall(int firstArgToEmit, C context, ObjExpr objx, GeneratorAdapter gen){ diff --git a/src/jvm/clojure/lang/MethodImplCache.java b/src/jvm/clojure/lang/MethodImplCache.java index 8d18a3b6..e4ed06d0 100644 --- a/src/jvm/clojure/lang/MethodImplCache.java +++ b/src/jvm/clojure/lang/MethodImplCache.java @@ -13,11 +13,24 @@ package clojure.lang; public final class MethodImplCache{ + +static public class Entry{ + final public Class c; + final public IFn fn; + + public Entry(Class c, IFn fn){ + this.c = c; + this.fn = fn; + } +} + public final IPersistentMap protocol; public final Keyword methodk; public final int shift; public final int mask; -public final Object[] table; //[class, fn. class, fn ...] +public final Object[] table; //[class, entry. class, entry ...] + +volatile Entry mre = null; public MethodImplCache(IPersistentMap protocol, Keyword methodk){ this(protocol, methodk, 0, 0, RT.EMPTY_ARRAY); @@ -32,12 +45,22 @@ public MethodImplCache(IPersistentMap protocol, Keyword methodk, int shift, int } public IFn fnFor(Class c){ + Entry last = mre; + if(last != null && last.c == c) + return last.fn; + return findFnFor(c); +} + +IFn findFnFor(Class c){ int idx = ((Util.hash(c) >> shift) & mask) << 1; if(idx < table.length && table[idx] == c) { - return (IFn) table[idx + 1]; + Entry e = ((Entry) table[idx + 1]); + mre = e; + return e != null ? e.fn : null; } return null; } + } |