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

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

+9
Java 交互
Clojure 的随机函数目前使用 {{Math.random}} 及相关功能,这使得它们无法进行设置。这似乎是使用动态 var(与额外参数相比)的一个适当用途,因为想要以随机方式行为的库代码可以透明地支持设置,而无需任何额外工作。

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

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

二、基准测试

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

使用的代码在 [https://github.com/gfredericks/clj-1452-tests;];;;;

{{rand}} 略慢,而 {{shuffle}} 则显著更快。在使用 8 个线程的 {{shuffle}} 中,没有显著的区别,但在修改版的补丁中手动切换到 {{ThreadLocalRandom}} 导致速度提高了 2.5 倍。

在我的 8 内核 Linode 虚拟机上运行

||基准||Clojure||运行时平均||运行时标准差||
|{{rand}}|1.6.0|61.3纳秒|7.06纳秒|
|{{rand}}|1.6.0 + {{*rand*}}|63.7纳秒|1.80纳秒|
|{{shuffle}}|1.6.0|12.9微秒|251纳秒|
|{{shuffle}}|1.6.0 + {{*rand*}}|12.8微秒|241纳秒|
|{{threaded-shuffling}}|1.6.0|151毫秒|2.31毫秒|
|{{threaded-shuffling}}|1.6.0 + {{*rand*}}|152毫秒|8.77毫秒|
|{{threaded-local-shuffling}}|1.6.0|N/A|N/A|
|{{threaded-local-shuffling}}|1.6.0 + {{*rand*}}|64.5毫秒|1.41毫秒|


方法:创建一个动态 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: Final GC required 1.261632096547911 % of runtime
求值计数: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 % 异常值引起方差轻微偏高

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 测试线程化的 shuffle ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
求值计数: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% 异常值导致方差轻微膨胀

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 测试线程化的 shuffle ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
求值计数: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% 异常值导致方差中度膨胀

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 测试thread-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.generators 文字:data.generators) 提供可播种随机数

0

评论由:gshayban发表

仅作备注,ThreadLocalRandom自JDK 7起可用。

0

评论者:gfredericks

{{ThreadLocalRandom}} 中自JDK7起-support-的问题是否不再存在,正确吗?

0

评论者:alexmiller

是的

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