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

欢迎!有关如何使用本站的更多信息,请参阅关于页面。

+9
Java 互操作
Clojure 的随机函数目前使用 {{Math.random}} 和相关功能,这使得它们无法设置种子。相比之下,使用动态变量(相比于额外参数)是一个合适的使用方式,因为希望表现出随机行为的库代码可以透明地支持设置种子,而无需额外的工作。

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

我认为这在语义上不会是破坏性更改。

h2. Criterium 基准测试

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

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

{{rand}} 稍微慢一些,而 {{shuffle}} 没有显著的差异。使用 {{shuffle}} 的 8 线程没有显著差异,但在补丁版本中手动切换到 {{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|


*方法:* 创建一个动态变量 *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

我认为线程本地问题很有趣,但不确定答案。

如果描述中包含测试结果表格,并在注释中包含criterium输出,那就更好了。

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 ;;
;;;;;;;;;;;;;;;;;;
警告:最终GC需要1.261632096547911 %的运行时间
评估次数:644646900在10744115调用中的60个样本中。
             平均执行时间:61.297605纳米秒
    执行时间标准偏差:7.057249纳米秒
   执行时间下四分位数:56.872437纳米秒(2.5%)
   执行时间上四分位数:84.483045纳米秒(97.5%)
                  使用的开销:16.319772纳米秒

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

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

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

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

;;;;;;;;;;;;;;;;;;
;; 测试rand ;;
;;;;;;;;;;;;;;;;;;
评估次数:781707720在60个样本中的13028462个调用中。
             执行时间平均值: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% 异常值适度增加方差

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 测试threaded-local-shuffling ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
评估次数: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

评论者:gfredericks

我认为使用ThreadLocal从逻辑上不依赖于添加逗号,所以它可以是单独的票据。我只是在提一下,因为它可以减少逗号的任何速度降低,但现在我再看看基准结果,这种减慢可能并不重要。

0

评论者:gfredericks

还值得注意的是,正如我在基准代码中做的那样,只使用补丁的变化(即,没有ThreadLocal),用户仍然能够手动实现ThreadLocal,目前这是不可能的。

0

评论者:stu

解决方案:(链接:[https://github.com/clojure/data.prng](https://github.com/clojure/data.prng) 文字:data.prng) 提供了可种子随机数

0

评论者:gshayban

只是提醒一下,ThreadLocalRandom 从 JDK 7 开始支持。

0

评论者:gfredericks

关于 ThreadLocalRandom 的 >= JDK7 方面,不再存在问题,对吗?

0

评论由:alexmiller

是的。

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