summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRich Hickey <richhickey@gmail.com>2010-04-19 15:07:13 -0400
committerRich Hickey <richhickey@gmail.com>2010-04-19 15:07:13 -0400
commite660e467789ccc8e9922948b3498939e0239fc7c (patch)
tree2010ebf34798141234344912b4a3f624743d39ab
parentccd7ae47ece97bed6b5eb39e5ba8779b214548cc (diff)
new perf for protocols
-rw-r--r--src/clj/clojure/core_deftype.clj43
-rw-r--r--src/jvm/clojure/lang/Compiler.java72
-rw-r--r--src/jvm/clojure/lang/MethodImplCache.java27
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;
}
+
}