目前`satisfies?`没有使用协议方法所用的相同实现缓存,这使得它在实际应用中变得非常慢。
使用
(defprotocol p (f [_]))
(deftype x [])
(deftype y [])
(extend-type x p (f [_]))
补丁之前
(let [s "abc"] (bench (instance? CharSequence s))) ;; 执行时间平均:1.358360 ns
(let [x (x.)] (bench (satisfies? p x))) ;; 执行时间平均:112.649568 ns
(let [y (y.)] (bench (satisfies? p y))) ;; 执行时间平均:2.605426 µs
*原因:* `satisfies?`通过调用`find-protocol-impl`来检查对象是否实现协议,这会检查x是否是协议接口的实例或x的类是否是协议实现之一(或是否在继承链中会使其成为真)。此检查相当昂贵且未缓存。
*建议:* 扩展协议的方法实现缓存,以处理(并缓存)实例检查(包括负结果)。
补丁之后
(let [x (x.)] (bench (satisfies? p x))) ;; 执行时间平均:79.321426 ns
(let [y (y.)] (bench (satisfies? p y))) ;; 执行时间平均:77.410858 ns
*补丁:* CLJ-1814-v7.patch (依赖于CLJ-2426)