请分享您的想法,参与 2024 Clojure 状况调查!

欢迎!有关更多信息,请参阅 关于页面。

+9
Java 互操作
Clojure 的随机函数目前使用 {{Math.random}} 和相关功能,这使得它们不可能被种子。这似乎是动态 var 的适当用途(与额外参数相比),因为希望行为随机化的库代码可以透明地支持种子而不需要任何额外努力。

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

我认为在语义上这不会是一个破坏性的改变。

h2. 测试基准

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

使用的代码在 [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|


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

*补丁:* CLJ-1452.patch

*审查人员:*

11 答案

0

评论者:gfredericks

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

0

评论者:gfredericks

在我们的代码改动期间,我们应该尝试将Clojure的随机函数默认设置为线程局部吗?我们可以创建一个自定义的{{Random}}子类,在其中包含{{ThreadLocal}}逻辑(因为Java 6中不存在{{ThreadLocalRandom}}),并将其设为默认值。

0

评论者:alexmiller

我认为线程局部问题很有意思,但不确定答案。

如果描述中包含一个总结测试结果的表格,并在注释中提供输出标准,那就更好了。

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 %)
    low-severe   1 (1.6667 %)
    low-mild     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 %)
    low-severe   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,在60个样本中,每个样本13028462次调用。
             执行时间平均:63.679152 ns
    执行时间标准差:1.798245 ns
   执行时间下四分位数:61.414851 纳秒(2.5%)
   执行时间上四分位数:67.412204 纳秒(97.5%)
                   使用的开销:13.008428 纳秒

在60个样本中找到3个异常值(5.0000 %)
    low-severe   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%)
    low-severe   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 %)
    low-severe   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%)
    low-severe   1 (1.6667 %)
 异常值引起的方差:9.4540% 异常值使方差轻微膨胀
0

评论者:gfredericks

我认为使用{{ThreadLocal}}与添加 }在逻辑上是独立的,所以它可以是一个单独的票据。我只是在这里提出建议,因为对于某些使用情况可以缓解 }带来的任何减慢,但现在我再次查看基准结果,减慢可能是微不足道的。

0

评论者:gfredericks

此外,值得一提的是,(就像我在基准代码中所做的那样)仅通过补丁的更改(即不涉及ThreadLocal),用户仍然可以手动使用ThreadLocal的能力,这在目前是不可能的。

0

颜色:stu

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

0

颜色:gshayban

只是提醒,ThreadLocalRandom是&alled; JDK 7。

0

评论者:gfredericks

{{ThreadLocalRandom}}的>=JDK7方面不再成问题,对吗?

0

评论者:alexmiller

对的。

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