Clojure函数的微基准测试

5

问题

assoc这样的小型Clojure函数有多快?我怀疑assoc的操作速度在100纳秒到3微秒之间,这使得计时变得困难。

使用time

user=> (def d {1 1, 2 2})
#'user/d
user=> (time (assoc d 3 3))
"Elapsed time: 0.04989 msecs"
{1 1, 2 2, 3 3}

显然这个基准测试中有很多开销,所以我不信任它。朋友们向我指出了Criterium,它处理了许多基准测试的痛点(多次评估、JVM预热、垃圾回收,请参见如何在Clojure中对函数进行基准测试?)。

使用Criterium

可悲的是,在这样一个小的基准测试中,甚至Criterium也失败了。

user=> (use 'criterium.core)
nil
user=> (def d {1 1 2 2})
#'user/d
user=> (bench (assoc d 3 3))
WARNING: JVM argument TieredStopAtLevel=1 is active, and may lead to unexpected results as JIT C2 compiler may not be active. See http://www.slideshare.net/CharlesNutter/javaone-2012-jvm-jit-for-dummies.
WARNING: Final GC required 1.694448681330372 % of runtime
Evaluation count : 218293620 in 60 samples of 3638227 calls.
             Execution time mean : -15.677491 ns
    Execution time std-deviation : 6.093770 ns
   Execution time lower quantile : -20.504699 ns ( 2.5%)
   Execution time upper quantile : 1.430632 ns (97.5%)
                   Overhead used : 123.496848 ns

如果你错过了,这个操作平均需要花费-15纳秒。我知道Clojure非常神奇,但负运行时间似乎太好了。

重复问题

那么,assoc需要多长时间?如何在Clojure中对微操作进行基准测试?


只是好奇,您想要提高应用程序的哪个部分的性能? - albusshin
我认为没有直接的答案。你可能想看一下今年Clojure演讲中Zach Tellman的演讲“可预测的快速Clojure”https://www.youtube.com/watch?v=iQwQXVM6oiY - Leon Grapenthin
@AlbusShin 目前我真的很关心 assoc 的性能。我对哈希映射在结构共享和复制编辑下的性能很感兴趣。 - MRocklin
3个回答

5

Criterium试图排除其自己的度量所带来的开销。这可能会导致快速函数出现负结果。请参阅自述文件中的测量开销估计部分。你的开销异常高。你可以运行(estimatated-overhead!) [sic]几次以采样更准确的开销数值。


5

为什么不用循环包装并重新归一化呢?

在我的硬件上,

(bench (dotimes [_ 1000] (assoc d 3 3)))

产生的平均执行时间大约是那个的1000倍。
(bench (assoc d 3 3))

具体来说,在第一种情况下约为100微秒,而在第二种情况下约为100纳秒。如果您的单个assoc在Criterium中处于“噪声”水平,您可以尝试以相同方式包装它,并且可以非常接近“内在”值。((dotimes [_ 1000] 1)的时钟速度为0.59微秒,因此循环本身所施加的额外开销相对较小。)


这会利用缓存结果吗?还是每个“assoc”调用都做了与第一个相等的工作? - MRocklin
从Clojure的角度来看,我认为你需要使用记忆化来实现缓存。当然,硬件对其缓存的使用是另一回事,JVM的细节也是如此。但是上面的测试讲述了一个相当简单的故事。 - JohnJ


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接