Skip to content

Commit e660e46

Browse files
committed
new perf for protocols
1 parent ccd7ae4 commit e660e46

File tree

3 files changed

+62
-80
lines changed

3 files changed

+62
-80
lines changed

src/clj/clojure/core_deftype.clj

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -357,14 +357,14 @@
357357
;;;;;;;;;;;;;;;;;;;;;;; protocols ;;;;;;;;;;;;;;;;;;;;;;;;
358358

359359
(defn- expand-method-impl-cache [#^clojure.lang.MethodImplCache cache c f]
360-
(let [cs (into {} (remove (fn [[c f]] (nil? f)) (map vec (partition 2 (.table cache)))))
361-
cs (assoc cs c f)
360+
(let [cs (into {} (remove (fn [[c e]] (nil? e)) (map vec (partition 2 (.table cache)))))
361+
cs (assoc cs c (clojure.lang.MethodImplCache$Entry. c f))
362362
[shift mask] (min-hash (keys cs))
363363
table (make-array Object (* 2 (inc mask)))
364-
table (reduce (fn [#^objects t [c f]]
364+
table (reduce (fn [#^objects t [c e]]
365365
(let [i (* 2 (int (shift-mask shift mask (hash c))))]
366366
(aset t i c)
367-
(aset t (inc i) f)
367+
(aset t (inc i) e)
368368
t))
369369
table cs)]
370370
(clojure.lang.MethodImplCache. (.protocol cache) (.methodk cache) shift mask table)))
@@ -412,9 +412,11 @@
412412
[protocol x]
413413
(boolean (find-protocol-impl protocol x)))
414414

415-
(defn -cache-protocol-fn [#^clojure.lang.AFunction pf x]
415+
(defn -cache-protocol-fn [#^clojure.lang.AFunction pf x #^Class c #^clojure.lang.IFn interf]
416416
(let [cache (.__methodImplCache pf)
417-
f (find-protocol-method (.protocol cache) (.methodk cache) x)]
417+
f (if (.isInstance c x)
418+
interf
419+
(find-protocol-method (.protocol cache) (.methodk cache) x))]
418420
(when-not f
419421
(throw (IllegalArgumentException. (str "No implementation of method: " (.methodk cache)
420422
" of protocol: " (:var (.protocol cache))
@@ -424,25 +426,30 @@
424426

425427
(defn- emit-method-builder [on-interface method on-method arglists]
426428
(let [methodk (keyword method)
427-
gthis (with-meta (gensym) {:tag 'clojure.lang.AFunction})]
429+
gthis (with-meta (gensym) {:tag 'clojure.lang.AFunction})
430+
ginterf (gensym)]
428431
`(fn [cache#]
429-
(let [#^clojure.lang.AFunction f#
432+
(let [~ginterf
433+
(fn
434+
~@(map
435+
(fn [args]
436+
(let [gargs (map #(gensym (str "gf__" % "__")) args)
437+
target (first gargs)]
438+
`([~@gargs]
439+
(. ~(with-meta target {:tag on-interface}) ~(or on-method method) ~@(rest gargs)))))
440+
arglists))
441+
#^clojure.lang.AFunction f#
430442
(fn ~gthis
431443
~@(map
432444
(fn [args]
433445
(let [gargs (map #(gensym (str "gf__" % "__")) args)
434446
target (first gargs)]
435447
`([~@gargs]
436-
(~@(if on-interface
437-
`(if (instance? ~on-interface ~target)
438-
(. ~(with-meta target {:tag on-interface}) ~(or on-method method) ~@(rest gargs)))
439-
`(do))
440-
(let [cache# (.__methodImplCache ~gthis)]
441-
;(assert cache#)
442-
(let [f# (or (.fnFor cache# (clojure.lang.Util/classOf ~target))
443-
(-cache-protocol-fn ~gthis ~target))]
444-
;(assert f#)
445-
(f# ~@gargs)))))))
448+
(let [cache# (.__methodImplCache ~gthis)
449+
f# (.fnFor cache# (clojure.lang.Util/classOf ~target))]
450+
(if f#
451+
(f# ~@gargs)
452+
((-cache-protocol-fn ~gthis ~target ~on-interface ~ginterf) ~@gargs))))))
446453
arglists))]
447454
(set! (.__methodImplCache f#) cache#)
448455
f#))))

src/jvm/clojure/lang/Compiler.java

Lines changed: 12 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2885,82 +2885,35 @@ else if(isDirect)
28852885
}
28862886

28872887
public void emitProto(C context, ObjExpr objx, GeneratorAdapter gen){
2888-
Label elseLabel = gen.newLabel();
2889-
Label notSameClassLabel = gen.newLabel();
2890-
Label faultLabel = gen.newLabel();
2891-
Label callLabel = gen.newLabel();
28922888
Label onLabel = gen.newLabel();
2889+
Label callLabel = gen.newLabel();
28932890
Label endLabel = gen.newLabel();
28942891

28952892
Var v = ((VarExpr)fexpr).var;
28962893

28972894
Expr e = (Expr) args.nth(0);
28982895
e.emit(C.EXPRESSION, objx, gen);
28992896
gen.dup(); //target, target
2897+
gen.invokeStatic(UTIL_TYPE,Method.getMethod("Class classOf(Object)")); //target,class
2898+
gen.loadThis();
2899+
gen.getField(objx.objtype, objx.cachedClassName(siteIndex),CLASS_TYPE); //target,class,cached-class
2900+
gen.visitJumpInsn(IF_ACMPEQ, callLabel); //target
29002901
if(protocolOn != null)
29012902
{
2903+
gen.dup(); //target, target
29022904
gen.instanceOf(Type.getType(protocolOn));
29032905
gen.ifZCmp(GeneratorAdapter.NE, onLabel);
2904-
gen.dup();
29052906
}
2906-
gen.invokeStatic(UTIL_TYPE,Method.getMethod("Class classOf(Object)")); //target,class
2907-
gen.dup(); //target,class,class
2908-
gen.loadThis();
2909-
gen.getField(objx.objtype, objx.cachedClassName(siteIndex),CLASS_TYPE); //target,class,class,cached-class
2910-
gen.visitJumpInsn(IF_ACMPNE, notSameClassLabel); //target,class
2911-
objx.emitVar(gen, v);
2912-
gen.invokeVirtual(VAR_TYPE, Method.getMethod("Object getRawRoot()")); //target, class, proto-fn
2913-
gen.dup(); //target, class, proto-fn, proto-fn
2914-
gen.loadThis();
2915-
gen.getField(objx.objtype, objx.cachedProtoFnName(siteIndex),AFUNCTION_TYPE); //target,class, proto-fn,proto-fn,cached-proto-fn
2916-
gen.visitJumpInsn(IF_ACMPNE, elseLabel); //target,class, proto-fn
2917-
gen.pop(); //target,class
2918-
gen.pop(); //target
2919-
gen.loadThis();
2920-
gen.getField(objx.objtype, objx.cachedProtoImplName(siteIndex),IFN_TYPE); //target,proto-impl
2921-
gen.swap(); //proto-impl, target
2922-
gen.goTo(callLabel);
2923-
29242907

2925-
gen.mark(notSameClassLabel); //target,class
2926-
gen.dup(); //target,class,class
2908+
gen.mark(callLabel); //target
2909+
gen.dup(); //target, target
2910+
gen.invokeStatic(UTIL_TYPE,Method.getMethod("Class classOf(Object)")); //target,class
29272911
gen.loadThis();
29282912
gen.swap();
2929-
gen.putField(objx.objtype, objx.cachedClassName(siteIndex),CLASS_TYPE); //target,class
2913+
gen.putField(objx.objtype, objx.cachedClassName(siteIndex),CLASS_TYPE); //target
29302914
objx.emitVar(gen, v);
2931-
gen.invokeVirtual(VAR_TYPE, Method.getMethod("Object getRawRoot()")); //target, class, proto-fn
2932-
2933-
gen.mark(elseLabel); //target, class, proto-fn
2934-
gen.checkCast(AFUNCTION_TYPE);
2935-
gen.dup(); //target,class,proto-fn,proto-fn
2936-
gen.loadThis();
2937-
gen.swap();
2938-
gen.putField(objx.objtype, objx.cachedProtoFnName(siteIndex),AFUNCTION_TYPE); //target, class, proto-fn
2939-
gen.dupX1(); //target, proto-fn, class, proto-fn
2940-
gen.getField(AFUNCTION_TYPE,"__methodImplCache", Type.getType(MethodImplCache.class)); //target,protofn,class,cache
2941-
gen.swap(); //target,protofn,cache,class
2942-
gen.invokeVirtual(Type.getType(MethodImplCache.class),Method.getMethod("clojure.lang.IFn fnFor(Class)")); //target,protofn,impl
2943-
gen.dup(); //target,protofn,impl, impl
2944-
gen.ifNull(faultLabel); //target,protofn,impl
2945-
gen.swap(); //target,impl, protofn
2946-
gen.pop(); //target, impl
2947-
gen.dup(); //target,impl, impl
2948-
gen.loadThis();
2915+
gen.invokeVirtual(VAR_TYPE, Method.getMethod("Object getRawRoot()")); //target, proto-fn
29492916
gen.swap();
2950-
gen.putField(objx.objtype, objx.cachedProtoImplName(siteIndex),IFN_TYPE); //target,impl
2951-
gen.swap(); //impl,target
2952-
gen.goTo(callLabel);
2953-
2954-
//not in fn table, null out cached fn and use proto-fn itself (which should seed table for next time)
2955-
gen.mark(faultLabel); //target,protofn,null
2956-
gen.pop(); //target, protofn
2957-
gen.swap(); //protofn, target
2958-
gen.loadThis();
2959-
gen.visitInsn(Opcodes.ACONST_NULL);
2960-
gen.putField(objx.objtype, objx.cachedProtoFnName(siteIndex), AFUNCTION_TYPE); //target, class, proto-fn
2961-
gen.goTo(callLabel);
2962-
2963-
gen.mark(callLabel); //impl, target
29642917
emitArgsAndCall(1, context,objx,gen);
29652918
gen.goTo(endLabel);
29662919

@@ -2976,9 +2929,8 @@ public void emitProto(C context, ObjExpr objx, GeneratorAdapter gen){
29762929
Method m = new Method(onMethod.getName(), Type.getReturnType(onMethod), Type.getArgumentTypes(onMethod));
29772930
gen.invokeInterface(Type.getType(protocolOn), m);
29782931
HostExpr.emitBoxReturn(objx, gen, onMethod.getReturnType());
2979-
}
2932+
}
29802933
gen.mark(endLabel);
2981-
29822934
}
29832935

29842936
void emitArgsAndCall(int firstArgToEmit, C context, ObjExpr objx, GeneratorAdapter gen){

src/jvm/clojure/lang/MethodImplCache.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,24 @@
1313
package clojure.lang;
1414

1515
public final class MethodImplCache{
16+
17+
static public class Entry{
18+
final public Class c;
19+
final public IFn fn;
20+
21+
public Entry(Class c, IFn fn){
22+
this.c = c;
23+
this.fn = fn;
24+
}
25+
}
26+
1627
public final IPersistentMap protocol;
1728
public final Keyword methodk;
1829
public final int shift;
1930
public final int mask;
20-
public final Object[] table; //[class, fn. class, fn ...]
31+
public final Object[] table; //[class, entry. class, entry ...]
32+
33+
volatile Entry mre = null;
2134

2235
public MethodImplCache(IPersistentMap protocol, Keyword methodk){
2336
this(protocol, methodk, 0, 0, RT.EMPTY_ARRAY);
@@ -32,12 +45,22 @@ public MethodImplCache(IPersistentMap protocol, Keyword methodk, int shift, int
3245
}
3346

3447
public IFn fnFor(Class c){
48+
Entry last = mre;
49+
if(last != null && last.c == c)
50+
return last.fn;
51+
return findFnFor(c);
52+
}
53+
54+
IFn findFnFor(Class c){
3555
int idx = ((Util.hash(c) >> shift) & mask) << 1;
3656
if(idx < table.length && table[idx] == c)
3757
{
38-
return (IFn) table[idx + 1];
58+
Entry e = ((Entry) table[idx + 1]);
59+
mre = e;
60+
return e != null ? e.fn : null;
3961
}
4062
return null;
4163
}
4264

65+
4366
}

0 commit comments

Comments
 (0)