请分享您的看法到 2024 年 Clojure 状态调查!

欢迎!请查看 关于 页面,了解有关此功能的一些更多信息。

+13
Protocols
当前 `satisfies?` 未使用与协议方法相同的 impl 缓存,因此在实际应用中速度过慢。

使用以下代码:

(defprotocol p (f [_]))
(deftype x [])
(deftype y [])
(extend-type x p (f [_]))


补丁之前

(let [s "abc"] (bench (instance? CharSequence s))) ;; 执行时间平均值:1.358360 纳秒
(let [x (x.)] (bench (satisfies? p x))) ;; 执行时间平均值:112.649568 纳秒
(let [y (y.)] (bench (satisfies? p y))) ;; 执行时间平均值:2.605426 微秒


*原因:* `satisfies?` 调用 `find-protocol-impl` 来检查对象是否实现协议,这会检查 x 是否是协议接口的实例,或者 x 的类是否是协议实现之一(或者如果它在会导致这种情况的继承链中)。这个检查相当昂贵,且没有缓存。

*建议:* 将协议的方法实现缓存扩展到也包括处理(并缓存)实例检查(包括负结果)。

补丁之后

(let [x (x.)] (bench (satisfies? p x))) ;; 执行时间平均值:79.321426 纳秒
(let [y (y.)] (bench (satisfies? p y))) ;; 执行时间平均值:77.410858 纳秒


*补丁*: CLJ-1814-v7.patch (依赖于 CLJ-2426)

25 个回答

0

评论者:michaelblume

不错。在重构它们之前,Honeysql 将80-90%的时间花在了satisfies?调用上。

0

评论者:michaelblume

我明白这是一个很难复现的严重错误,但如果我克隆core.match库,将其Clojure依赖指向1.8.0-master-SNAPSHOT版本,启动REPL,然后从vim连接到REPL并重新加载clojure.core.match,我会得到以下结果:

`
|| java.lang.Exception: 未找到命名空间 'clojure.tools.analyzer.jvm.utils',编译:(clojure/tools/analyzer/jvm.clj:9:1)
zipfile:/用户/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|5647| clojure.core$throw_if.invokeStatic
zipfile:/用户/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|5733| clojure.core$load_lib.invokeStatic
|| clojure.core$load_lib.doInvoke(core.clj)
|| clojure.lang.RestFn.applyTo(RestFn.java:142)
zipfile:/用户/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|647| clojure.core$apply.invokeStatic
zipfile:/用户/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|5765| clojure.core$load_libs.invokeStatic
|| clojure.core$load_libs.doInvoke(core.clj)
|| clojure.lang.RestFn.applyTo(RestFn.java:137)
zipfile:/用户/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|647| clojure.core$apply.invokeStatic
zipfile:/用户/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|5887| clojure.core$require.invokeStatic
|| clojure.core$require.doInvoke(core.clj)
|| clojure.lang.RestFn.invoke(RestFn.java:703)
zipfile:/用户/michael.blume/.m2/repository/org/clojure/tools.analyzer.jvm/0.6.5/tools.analyzer.jvm-0.6.5.jar::clojure/tools/analyzer/jvm.clj|9| clojure.tools.analyzer.jvm$eval4968$loading5561auto__4969.invoke
zipfile:/用户/michael.blume/.m2/repository/org/clojure/tools.analyzer.jvm/0.6.5/tools.analyzer.jvm-0.6.5.jar::clojure/tools/analyzer/jvm.clj|9| clojure.tools.analyzer.jvm$eval4968.invokeStatic
|| clojure.tools.analyzer.jvm$eval4968.invoke(jvm.clj)
|| clojure.lang.Compiler.eval(Compiler.java:6934)
|| clojure.lang.Compiler.eval(Compiler.java:6923)
|| clojure.lang.Compiler.load(Compiler.java:7381)
|| clojure.lang.RT.loadResourceScript(RT.java:372)
|| clojure.lang.RT.loadResourceScript(RT.java:363)
|| clojure.lang.RT.load(RT.java:453)
|| clojure.lang.RT.load(RT.java:419)
zipfile:/用户/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|5883| clojure.core$load$fn5669.invoke
zipfile:/用户/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|5882| clojure.core$load.invokeStatic
zipfile:/用户/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|5683| clojure.core$load_one.invokeStatic
|| clojure.core$load_one.invoke(core.clj)
zipfile:/用户/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|5728| clojure.core$load_lib$fn5618.invoke
zipfile:/用户/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|5727| clojure.core$load_lib.invokeStatic
|| clojure.core$load_lib.doInvoke(core.clj)
|| clojure.lang.RestFn.applyTo(RestFn.java:142)
zipfile:/用户/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|647| clojure.core$apply.invokeStatic
zipfile:/用户/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|5765| clojure.core$load_libs.invokeStatic
|| clojure.core$load_libs.doInvoke(core.clj)
|| clojure.lang.RestFn.applyTo(RestFn.java:137)
zipfile:/用户/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|647| clojure.core$apply.invokeStatic
zipfile:/用户/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|5787| clojure.core$require.invokeStatic
|| clojure.core$require.doInvoke(core.clj)
|| clojure.lang.RestFn.invoke(RestFn.java:457)
src/main/clojure/clojure/core/match.clj|1| clojure.core.match$eval4960$loading
5561auto__4961.invoke
src/main/clojure/clojure/core/match.clj|1| clojure.core.match$eval4960.invokeStatic
|| clojure.core.match$eval4960.invoke(match.clj)
|| clojure.lang.Compiler.eval(Compiler.java:6934)
|| clojure.lang.Compiler.eval(Compiler.java:6923)
|| clojure.lang.Compiler.load(Compiler.java:7381)
|| clojure.lang.RT.loadResourceScript(RT.java:372)
|| clojure.lang.RT.loadResourceScript(RT.java:363)
|| clojure.lang.RT.load(RT.java:453)
|| clojure.lang.RT.load(RT.java:419)
zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|5883| clojure.core$load$fn5669.invoke
zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|5882| clojure.core$load.invokeStatic
zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|5683| clojure.core$load_one.invokeStatic
|| clojure.core$load_one.invoke(core.clj)
zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|5728| clojure.core$load_lib$fn5618.invoke
zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|5727| clojure.core$load_lib.invokeStatic
|| clojure.core$load_lib.doInvoke(core.clj)
|| clojure.lang.RestFn.applyTo(RestFn.java:142)
zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|647| clojure.core$apply.invokeStatic
zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|5765| clojure.core$load_libs.invokeStatic
|| clojure.core$load_libs.doInvoke(core.clj)
|| clojure.lang.RestFn.applyTo(RestFn.java:137)
zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|647| clojure.core$apply.invokeStatic
zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|5787| clojure.core$require.invokeStatic
|| clojure.core$require.doInvoke(core.clj)
|| clojure.lang.RestFn.invoke(RestFn.java:421)
|| clojure.core.match$eval4949.invokeStatic(form-init2494799382238714928.clj:1)
|| clojure.core.match$eval4949.invoke(form-init2494799382238714928.clj)
|| clojure.lang.Compiler.eval(Compiler.java:6934)
|| clojure.lang_compiler.eval(Compiler.java:6897)
zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|3096| clojure.core$eval.invokeStatic
|| clojure.core$eval.invoke(core.clj)
zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/main.clj|240| clojure.main$repl$read_eval_print
7404$fn7407.invoke
zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/main.clj|240| clojure.main$repl$read_eval_print
7404.invoke
zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/main.clj|258| clojure.main$repl$fn7413.invoke
zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/main.clj|258| clojure.main$repl.invokeStatic
|| clojure.main$repl.doInvoke(main.clj)
|| clojure.lang.RestFn.invoke(RestFn.java:1523)
zipfile:/Users/michael.blume/.m2/repository/org/clojure/tools.nrepl/0.2.10/tools.nrepl-0.2.10.jar::clojure/tools/nrepl/middleware/interruptible_eval.clj|58| clojure.tools.nrepl.middleware.interruptible_eval$evaluate$fn
637.invoke
|| clojure.lang.AFn.applyToHelper(AFn.java:152)
|| clojure.lang.AFn.applyTo(AFn.java:144)
zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|645| clojure.core$apply.invokeStatic
zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|1874| clojure.core$with_bindingsSTAR.invokeStatic
|| clojure.core$with_bindingsSTAR.doInvoke(core.clj)
|| clojure.lang.RestFn.invoke(RestFn.java:425)
zipfile:/Users/michael.blume/.m2/repository/org/clojure/tools.nrepl/0.2.10/tools.nrepl-0.2.10.jar::clojure/tools/nrepl/middleware/interruptible_eval.clj|56| clojure.tools.nrepl.middleware.interruptible_eval$evaluate.invokeStatic
|| clojure.tools.nrepl.middleware.interruptible_eval$evaluate.invoke(interruptible_eval.clj)
zipfile:/Users/michael.blume/.m2/repository/org/clojure/tools.nrepl/0.2.10/tools.nrepl-0.2.10.jar::clojure/tools/nrepl/middleware/interruptible_eval.clj|191| clojure.tools.nrepl.middleware.interruptible_eval$interruptible_eval$fn
679$fn__682.invoke
zipfile:/Users/michael.blume/.m2/repository/org/clojure/tools.nrepl/0.2.10/tools.nrepl-0.2.10.jar::clojure/tools/nrepl/middleware/interruptible_eval.clj|159| clojure.tools.nrepl.middleware.interruptible_eval$run_next$fn__674.invoke
|| clojure.lang.AFn.run(AFn.java:22)
|| java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
|| java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
|| java.lang.Thread.run(Thread.java:745)

`

我的项目在重新加载包含 clojure.core.match 的命名空间时也会出现相同的情况

0
by

评论人:bronsa

是否可能与 AOT 有关?

0

评论者:michaelblume

稍微缩小范围,如果我查看 tools.analyzer.jvm,打开一个 REPL,并执行 (require 'clojure.tools.analyzer.jvm.utils),我会得到:

|| java.lang.ClassCastException: java.lang.Class cannot be cast to clojure.asm.Type, compiling:(utils.clj:260:13) || clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3642) || clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3636) || clojure.lang.Compiler$DefExpr.eval(Compiler.java:450) || clojure.lang.Compiler.eval(Compiler.java:6939) || clojure.lang.Compiler.load(Compiler.java:7381) || clojure.lang.RT.loadResourceScript(RT.java:372) || clojure.lang.RT.loadResourceScript(RT.java:363) || clojure.lang.RT.load(RT.java:453) || clojure.lang.RT.load(RT.java:419) zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|5883| clojure.core$load$fn__5669.invoke zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|5882| clojure.core$load.invokeStatic zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|5683| clojure.core$load_one.invokeStatic || clojure.core$load_one.invoke(core.clj) zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|5728| clojure.core$load_lib$fn__5618.invoke zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|5727| clojure.core$load_lib.invokeStatic || clojure.core$load_lib.doInvoke(core.clj) || clojure.lang.RestFn.applyTo(RestFn.java:142) zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|647| clojure.core$apply.invokeStatic zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|5765| clojure.core$load_libs.invokeStatic || clojure.core$load_libs.doInvoke(core.clj) || clojure.lang.RestFn.applyTo(RestFn.java:137) zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|647| clojure.core$apply.invokeStatic zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|5787| clojure.core$require.invokeStatic || clojure.core$require.doInvoke(core.clj) || clojure.lang.RestFn.invoke(RestFn.java:421) || clojure.tools.analyzer.jvm.utils$eval4392.invokeStatic(form-init8663423518975891793.clj:1) || clojure.tools.analyzer.jvm.utils$eval4392.invoke(form-init8663423518975891793.clj) || clojure.lang.Compiler.eval(Compiler.java:6934) || clojure.lang.Compiler.eval(Compiler.java:6897) zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|3096| clojure.core$eval.invokeStatic || clojure.core$eval.invoke(core.clj) zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/main.clj|240| clojure.main$repl$read_eval_print__7404$fn__7407.invoke zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/main.clj|240| clojure.main$repl$read_eval_print__7404.invoke zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/main.clj|258| clojure.main$repl$fn__7413.invoke zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/main.clj|258| clojure.main$repl.invokeStatic || clojure.main$repl.doInvoke(main.clj) || clojure.lang.RestFn.invoke(RestFn.java:1523) zipfile:/Users/michael.blume/.m2/repository/org/clojure/tools.nrepl/0.2.10/tools.nrepl-0.2.10.jar::clojure/tools/nrepl/middleware/interruptible_eval.clj|58| clojure.tools.nrepl.middleware.interruptible_eval$evaluate$fn__637.invoke || clojure.lang.AFn.applyToHelper(AFn.java:152) || clojure.lang.AFn.applyTo(AFn.java:144) zipfile:/Users/michael.blume/.m2/repository/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|645| clojure.core$apply.invokeStatic zipfile:/Users/michael.blume/.m2/repository/or

我不确定 AOT(预编译)与这个问题有什么关系?

0

评论人:bronsa

(链接: ~michaelblume) 更新的补丁应该会修复您报告的问题

0

评论者:michaelblume

太好了,谢谢 =)

新补丁不再删除MethodImplCache,这不是故意的吗?

0

由:alexmiller 评论

如果描述中有一个关于补丁中变更的项目的列表,那就太好了。例如:“将MethodImplCache重命名为ImplCache”,这样有助于审查。

0

评论人:bronsa

附带的更新补丁不再用ImplCache替代MethodImplCache,而是简单地重用MethodImplCache,降低了补丁的影响,使其更容易(更安全)地审查。

0
_由:alexmiller_ 评论

由于这项在新{{inst?}}谓词中使用,提高优先级 - 查看 https://github.com/clojure/clojure/commit/58227c5de080110cb2ce5bc9f987d995a911b13e
0
_由:alexmiller_ 评论

我在v3补丁前后运行了测试。之前的测试时间相差不大,但我无法重现之后的测试结果。我得到了这个结果,其实在实际未找到的情况下更糟糕


(let [x (x.)] (bench (satisfies? p x))) ;; 平均执行时间:76.833504纳秒
(let [y (y.)] (bench (satisfies? p y))) ;; 平均执行时间:20.570007微秒

0
by
评论人:bronsa

v4 补丁修复了找不到的情况下的回归问题,不清楚为什么会发生这种情况,对此表示歉意。
以下是我现在得到的定时信息

clojure master

user=> (let [x (x.)] (bench (satisfies? p x)))
评估计数:604961580 在 60 个样本的 10082693 次调用中。
             平均执行时间:112.649568 ns
    执行时间标准差:12.216782 ns
   下四分位数执行时间:99.299203 ns (2.5%)
   上四分位数执行时间:144.265205 ns (97.5%)
                    额外开销:1.898271 ns

在 60 个样本中发现了 3 个异常值 (5.0000%)
    低-严重      2 (3.3333%)
    低-轻微      1 (1.6667%)
 异常值引起的方差:73.7545% 异常值严重影响了方差
nil




user=> (let [y (y.)] (bench (satisfies? p y)))
评估计数:22676100 在 60 个样本的 377935 次调用中。
             平均执行时间:2.605426 µs
    执行时间标准差:141.100070 ns
   下四分位数执行时间:2.487234 µs (2.5%)
   上四分位数执行时间:2.873045 µs (97.5%)
                    额外开销:1.898271 ns

在 60 个样本中发现了 1 个异常值 (1.6667%)
    低-严重      1 (1.6667%)
 异常值引起的方差:40.1251% 异常值适度影响方差
nil



master + v4

user=> (let [x (x.)] (bench (satisfies? p x)))
评估计数:731759100 在 60 个样本的 12195985 次调用中。
             平均执行时间:79.321426 ns
    执行时间标准差:3.959245 ns
   下四分位数执行时间:75.365187 ns (2.5%)
   上四分位数执行时间:87.986479 ns (97.5%)
                    额外开销:1.905711 ns

在 60 个样本中发现了 1 个异常值 (1.6667%)
    低-严重      1 (1.6667%)
 异常值引起的方差:35.2614% 异常值适度影响方差
nil



user=> (let [y (y.)] (bench (satisfies? p y)))
评估计数:771220140 在 60 个样本的 12853669 次调用中。
             平均执行时间:77.410858 ns
    执行时间标准差:1.407926 ns
   下四分位数执行时间:75.852530 ns (2.5%)
   上四分位数执行时间:80.759226 ns (97.5%)
                    额外开销:1.897646 ns

在 60 个样本中发现了 4 个异常值 (6.6667%)
    低-严重      3 (5.0000%)
    低-轻微      1 (1.6667%)
 异常值引起的方差:7.7866% 异常值稍微影响了方差


总结一下
master found = 112ns
master not-found = 2.6us

master+v4 found = 79ns
master+v4 not-found = 77ns
0
by

评论者:michaelblume

对于已经声明了特定协议实现的记录,并且因此实现了相应的接口,是否有必要使用(实例?)检查该接口作为快速路径?

0
by

由:alexmiller 评论

Michael - 这个检查已经在里面了

Nicola - 我有一些评论/问题

  1. 我不明白NIL材料的目的,你能解释一下吗?
  2. 在x是接口实例的情况下,旧代码返回x,而find-protocol-impl*中新的代码返回interface。为什么做出这个改变?
  3. 这个:(alter-var-root (:var protocol) assoc :impl-cache (expand-method-impl-cache cache c impl)) 在据我看似乎不是线程安全的 - 我觉得两个不同线程的不同impl同时缺失会导致缓存只有一个。这可能不会发生,也可能不是什么大问题,因为缓存将在下一个调用时更新(不会给出错误的答案),但我想提一下。我觉得没有简单的方法可以避免它而不进行很多修改。
0
by

评论人:bronsa

Alex,谢谢您查看这个问题
1- NIL对象是方法impl缓存中nil的一个占位符,因为find-and-cache-protocol-impl测试nil?以了解是否已缓存分发

2- 这个改变完全是出于一致性考虑,使find-and-cache-protocol-impl始终返回一个类/接口,而不仅仅是类/接口或具体实例。行为上没有变化,因为find-protocol-impl的两个消费者,即find-protocol-methodsatisfies?,在这种情况下对那个值不感兴趣

3- 是的,你是对的,这不是线程安全的,但我认为这是一个合理的权衡,因为它不会造成任何错误的行为。在最坏的情况下,只会多一次缓存缺失,而要使其线程安全,则意味着在每次缓存命中/缺失中的额外性能损失

0
by

评论者:michaelblume

(链接:) 我看到这个补丁有一些行为上的改变

`
(defprotocol BoolProtocol
(proto-fn [this]))

(extend-protocol BoolProtocol
Object
(proto-fn [x] "Object impl")

nil
(proto-fn [x] "Nil impl"))

(proto-fn false)
`

在Clojure master上使用这个补丁后返回 "Object impl",而使用这个补丁返回 "Nil impl"

...