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

欢迎!请参阅 关于页面 获取更多关于此功能的详细信息。

+9
Java 互操作
Clojure 的随机函数目前使用 {{Math.random}} 及相关功能,这使得它们无法进行播种。这似乎是使用动态 var(相对于额外参数)的一个适当的用法,因为希望表现出随机性的库代码可以透明地支持播种,而不需要任何额外的工作。

我建议在 {{clojure.core}} 中定义 {{(def ^:dynamic *rand* (java.util.Random.))}},并更新 {{rand}}、{{rand-int}}、{{rand-nth}} 和 {{shuffle}} 以使用 {{*rand*}}。

我认为这从语义上来说不会是一个破坏性变更。

h2. Criterium 基准测试

我进行了一些基准测试,试图了解使用动态 var 的性能影响,以及衡量并发访问的变化。

使用的代码位于 [https://github.com/gfredericks/clj-1452-tests];原始输出在注释中。

{{rand}} 略慢,而 {{shuffle}} 则微快。从 8 个线程使用 {{shuffle}} 略慢,但手动切换到修正版本中的 {{ThreadLocalRandom}} 可以带来 2.5 倍的速度提升。

在 8 核 Linode 虚拟机上运行

||基准测试||Clojure||运行平均时间||运行标准差||
|{{rand}}|1.6.0|61.3ns|7.06ns|
|{{rand}}|1.6.0 + {{*rand*}}|63.7ns|1.80ns|
|{{shuffle}}|1.6.0|12.9µs|251ns|
|{{shuffle}}|1.6.0 + {{*rand*}}|12.8µs|241ns|
|{{threaded-shuffling}}|1.6.0|151ms|2.31ms|
|{{threaded-shuffling}}|1.6.0 + {{*rand*}}|152ms|8.77ms|
|{{threaded-local-shuffling}}|1.6.0|N/A|N/A|
|{{threaded-local-shuffling}}|1.6.0 + {{*rand*}}|64.5ms|1.41ms|


*方法:创建一个动态 var *rand* 并更新 {{rand}}、{{rand-int}}、{{rand-nth}} 和 {{shuffle}} 以使用 {{*rand*}}

*修正:CLJ-1452.patch

*筛选:*

11 个回答

0

评论来自:gfredericks

附件 CLJ-1452.patch,包含与基准测试中相同的使用代码。

0

评论来自:gfredericks

当我们在这个问题上动手时,我们是否应该尝试将Clojure的随机函数默认设置为线程局部?我们可以有一个自定义的{{Random}}子类,在其中具有{{ThreadLocal}}逻辑(避免{{ThreadLocalRandom}}因为Java 6),并使其成为* }的默认值。

0

评论由:alexmiller

我觉得ThreadLocal问题很有趣,但是不确定答案。

如果描述可以以表格形式总结测试结果,并且将输出标准放在评论中会更好。

0
评论由:gfredericks

测试仓库存输出(在描述中的表格中已总结)


$ echo "Clojure 1.6.0"; lein with-profile +clj-1.6 run; echo "Clojure 1.6.0 with *rand*"; lein with-profile +clj-1452 run
Clojure 1.6.0

;;;;;;;;;;;;;;;;;;
;; 测试 rand ;;
;;;;;;;;;;;;;;;;;;
WARNING: 最终GC需要1.261632096547911 %的运行时间
评估次数:644646900 次在 60 次样本中的 10744115 次调用。
             执行时间平均值:61.297605 ns
    执行时间标准偏差:7.057249 ns
   执行时间下四分位数:56.872437 ns ( 2.5%)
   执行时间上四分位数:84.483045 ns (97.5%)
                   开销使用:16.319772 ns

在 60 次样本中发现 6 个异常值(10.0000 %)
    低严重   1 (1.6667 %)
    低轻微     5 (8.3333 %)
 异常值方差:75.5119 % 方差严重受异常值影响

;;;;;;;;;;;;;;;;;;;;;
;; 测试 shuffle ;;
;;;;;;;;;;;;;;;;;;;;;
评估次数:4780800 次在 60 次样本中的 79680 次调用。
             执行时间平均值:12.873832 µs
    执行时间标准偏差:251.388257 ns
   执行时间下四分位数:12.526871 µs ( 2.5%)
   执行时间上四分位数:13.417559 µs (97.5%)
                   开销使用:16.319772 ns

在 60 次样本中发现 3 个异常值(5.0000 %)
    低严重   3 (5.0000 %)
 异常值方差:7.8591 % 方差轻微受异常值影响

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 测试 threaded-shuffling ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
评估次数:420 次在 60 次样本中的 7 次调用。
             执行时间平均值:150.863290 ms
    执行时间标准偏差:2.313755 ms
   执行时间下四分位数:146.621548 ms ( 2.5%)
   执行时间上四分位数:155.218897 ms (97.5%)
                   开销使用:16.319772 ns
Clojure 1.6.0 with *rand*

;;;;;;;;;;;;;;;;;;
;; 测试 rand ;;
;;;;;;;;;;;;;;;;;;
评估次数:781707720 次,在 13028462 次调用中取 60 个样本。
             平均执行时间:63.679152 纳秒
    执行时间标准差:1.798245 纳秒
   执行时间下四分位数:61.414851 纳秒(2.5%)
   执行时间上四分位数:67.412204 纳秒(97.5%)
                   开销使用:13.008428 纳秒

在 60 次样本中发现 3 个异常值(5.0000 %)
    低严重   3 (5.0000 %)
 离群值方差:15.7596%,方差由于离群值中等程度膨胀

;;;;;;;;;;;;;;;;;;;;;
;; 测试 shuffle ;;
;;;;;;;;;;;;;;;;;;;;;
评估次数:4757940 次,在 79299 次调用中取 60 个样本。
             平均执行时间:12.780391 微秒
    执行时间标准差:240.542151 纳秒
   执行时间下四分位数:12.450218 微秒(2.5%)
   执行时间上四分位数:13.286910 微秒(97.5%)
                   开销使用:13.008428 纳秒

在 60 个样本中发现 1 个离群值(1.6667%)
    低严重   1 (1.6667 %)
 离群值方差:7.8228%,方差由于离群值轻微膨胀

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 测试 threaded-shuffling ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
评估次数:420 次在 60 次样本中的 7 次调用。
             平均执行时间:152.471310 毫秒
    执行时间标准差:8.769236 毫秒
   执行时间下四分位数:147.954789 毫秒(2.5%)
   执行时间上四分位数:161.277200 毫秒(97.5%)
                   开销使用:13.008428 纳秒

在 60 次样本中发现 3 个异常值(5.0000 %)
    低严重   3 (5.0000 %)
 离群值方差:43.4058%,方差由于离群值中等程度膨胀

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 测试线程局部洗牌 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
评估次数:960 次,在 16 次调用中取 60 个样本。
             平均执行时间:64.462853 毫秒
    执行时间标准差:1.407808 毫秒
   执行时间下四分位数:62.353265 毫秒(2.5%)
   执行时间上四分位数:67.197368 毫秒(97.5%)
                   开销使用:13.008428 纳秒

在 60 个样本中发现 1 个离群值(1.6667%)
    低严重   1 (1.6667 %)
 离群值方差:9.4540%,方差由于离群值轻微膨胀
0
by

评论来自:gfredericks

我认为使用 {{ThreadLocal}} 与添加 } 逻辑上独立,所以它可以是一个单独的工单。我只是在这里建议它,因为对于某些用途可以缓解任何由 } 造成的减慢,但现在我再次查看基准测试结果时,减慢可能并不重要。

0
by

评论来自:gfredericks

还值得注意的是(正如我在基准代码中所做的那样),仅通过补丁的更改(即,不涉及 ThreadLocal)用户仍然能够手动执行 ThreadLocal,这目前是不可能的。

0
by

评论由:stu 发布

修复方案:(链接:https://github.com/clojure/data.generators 文字:data.generators)提供可播种的随机数

0

评论者:gshayban

仅仅是记录一下,ThreadLocalRandom 替换大于等于 JDK 7。

0

评论来自:gfredericks

ThreadLocalRandom 大于等于 JDK7 的特性不再是一个问题,对吗?

0

评论由:alexmiller

是的

0
参考: https://clojure.atlassian.net/browse/CLJ-1452(由gfredericks报告)
...