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

欢迎!请参阅 关于 页以了解此工作的更多信息。

+9
Java 互操作
Clojure 的随机函数目前使用 {{Math.random}} 以及相关功能,这使得它们无法进行种子设置。这似乎是动态变量(相对于额外的参数)的合适用途,因为希望表现得随机的库代码可以透明地支持种子设置,而无需额外的努力。

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

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

h2. 评判标准基准

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

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


*方法:* 创建一个动态变量 *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 ;;
;;;;;;;;;;;;;;;;;;
警告:最终 GC 需要 1.261632096547911 % 的运行时间
评估次数:644646900 在 60 个样本中,每个样本 10744115 次调用。
             执行时间平均值: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 % 异常值对身体重的影响中等

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 测试线程局部排序 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
评估次数: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

workaround:(链接:https://github.com/clojure/data.generated 文本:data.generated)提供可播种的随机数

0
by

评论由:gshayban 发表

只是做个注释,ThreadLocalRandom自JDK 7开始可用。

0

由 gfredericks 发布的评论

关于 ThreadLocalRandom 在 JDK 7 及以上的特点已经不再是问题,对吗?

0

评论者: alexmiller

是的

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