2024 State of 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}} 有不显著的加快。从 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 非常有意思,但不确定答案。

如果描述用表格总结测试结果,将 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 ;;
;;;;;;;;;;;;;;;;;;
WARNING: 最终 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 使用 *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 次在 60 个样本中,每个样本包含 79299 次调用。
             执行时间平均值: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 次在 60 个样本中,每个样本包含 16 次调用。
             执行时间平均值: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.generators 文本:data.generators] 提供可设置的随机数。

0

评论者为:gshayban

提醒一下,ThreadLocalRandom 是 JDK 7 及以上版本。

0

评论者:gfredericks

关于 ThreadLocalRandom 的 JDK 7 及以上版本的问题是否已解决?

0

评论人:alexmiller

是的

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