2024年Clojure调查问卷中分享您的想法!

欢迎!有关该页面如何工作的更多信息,请参阅关于页面。

+13
协议
目前`satisfies?`没有使用与协议方法相同的实现缓存,这使得它在实际使用中速度过慢。

使用

(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的类是否是协议实现之一(或者如果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在`satisfies?`调用上花费了80-90%的时间。

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:/Users/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:/Users/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:/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:703)
zipfile:/Users/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:/Users/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:/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$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
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

评论者: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/org/clojure/clojure/1.8.0-master-SNAPSHOT/clojure-1.8.0-master-SNAPSHOT.jar::clojure/core.clj|1874| clojure.core$with_bindings_STAR_.invokeStatic || clojure.core$with_bindings_STAR_.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)

我看不出 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 ns
(let [y (y.)] (bench (satisfies? p y))) ;; 执行时间平均值:20.570007 µs

0
_评论由:bronsa_发表

v4补丁修复了找不到的情况下的回归问题,不清楚为什么会出现这种情况,对此表示歉意。
以下是现在的测试时间

clojure主分支

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

在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微秒
    标准差:141.100070纳秒
   下四分位数执行时间:2.487234微秒(2.5%)
   上四分位数执行时间:2.873045微秒(97.5%)
               使用开销:1.898271纳秒

在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纳秒
    标准差:3.959245纳秒
   下四分位数执行时间:75.365187纳秒(2.5%)
   上四分位数执行时间:87.986479纳秒(97.5%)
               使用开销:1.905711纳秒

在60个样本中发现1个异常值(1.6667%)
    低严重度     1(1.6667%)
 异常值的方差:35.2614%,方差因异常值而适度膨胀
nil



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

在60个样本中发现4个异常值(6.6667%)
    低严重度     3(5.0000%)
    低轻度     1(1.6667%)
 异常值的方差:7.7866%,方差因异常值而轻微膨胀


总结如下
master分支找到:112ns
master分支找不到:2.6us

master分支+v4找到:79ns
master分支+v4找不到:77ns
0

评论由:michaelblume

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

0
by

评论者:alexmiller

Michael - 该检查已经在那里了

Nicola - 我有一些评论/问题

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

评论者:bronsa

Alex,感谢你查看这个问题
1- NIL对象是方法实现缓存中的nil的占位符,因为find-and-cache-protocol-impl测试nil?以确定是否已缓存了委派

2- 这种变化只是一个一致性变化,使find-and-cache-protocol-impl总是返回一个类/接口而不是一个类/接口或具体实例。行为上,它没有任何变化,因为find-protocol-impl的两个消费者,即find-protocol-method和satisfies?,在这种情况下都不在乎那个值是什么

3- 是的,你是对的,它不是线程安全的,但我认为这是一个合理的权衡,因为它不会导致任何错误的行为,并且最坏的情况会导致一个额外的缓存缺失,使它线程安全将意味着在每个缓存命中/缺失处都会有一个额外的性能损失

0
by

评论由:michaelblume

(链接: ~bronsa) 我看到这个补丁导致的行 为发生了变化

`
(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"。

...