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的随机函数设置为线程局部?我们可以有一个具有{{ThreadLocal}}逻辑的{{Random}}子类(避免使用{{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

;;;;;;;;;;;;;;;;;;
;; Testing rand ;;
;;;;;;;;;;;;;;;;;;
WARNING: Final GC required 1.261632096547911 % of runtime
Evaluation count : 644646900 in 60 samples of 10744115 calls.
             Execution time mean : 61.297605 ns
    Execution time std-deviation : 7.057249 ns
   Execution time lower quantile : 56.872437 ns ( 2.5%)
   Execution time upper quantile : 84.483045 ns (97.5%)
                   Overhead used : 16.319772 ns

Found 6 outliers in 60 samples (10.0000 %)
    low-severe   1 (1.6667 %)
    low-mild     5 (8.3333 %)
 Variance from outliers : 75.5119 % Variance is severely inflated by outliers

;;;;;;;;;;;;;;;;;;;;;
;; Testing shuffle ;;
;;;;;;;;;;;;;;;;;;;;;
Evaluation count : 4780800 in 60 samples of 79680 calls.
             Execution time mean : 12.873832 µs
    Execution time std-deviation : 251.388257 ns
   Execution time lower quantile : 12.526871 µs ( 2.5%)
   Execution time upper quantile : 13.417559 µs (97.5%)
                   Overhead used : 16.319772 ns

Found 3 outliers in 60 samples (5.0000 %)
    low-severe   3 (5.0000 %)
 Variance from outliers : 7.8591 % Variance is slightly inflated by outliers

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Testing threaded-shuffling ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Evaluation count : 420 in 60 samples of 7 calls.
             Execution time mean : 150.863290 ms
    Execution time std-deviation : 2.313755 ms
   Execution time lower quantile : 146.621548 ms ( 2.5%)
   Execution time upper quantile : 155.218897 ms (97.5%)
                   Overhead used : 16.319772 ns
Clojure 1.6.0 with *rand*

;;;;;;;;;;;;;;;;;;
;; Testing rand ;;
;;;;;;;;;;;;;;;;;;
Evaluation count : 781707720 in 60 samples of 13028462 calls.
             执行时间平均值:63.679152 ns
    执行时间标准偏差:1.798245 ns
   执行时间下四分位数:61.414851 ns ( 2.5%)
   执行时间上四分位数:67.412204 ns (97.5%)
                   开销使用:13.008428 ns

Found 3 outliers in 60 samples (5.0000 %)
    low-severe   3 (5.0000 %)
 异常值方差:15.7596 % 异常值适度增加了方差

;;;;;;;;;;;;;;;;;;;;;
;; Testing shuffle ;;
;;;;;;;;;;;;;;;;;;;;;
评估次数:4757940 次,来自 79299 次调用中的 60 个样本。
             执行时间平均值:12.780391 µs
    执行时间标准偏差:240.542151 ns
   执行时间下四分位数:12.450218 µs ( 2.5%)
   执行时间上四分位数:13.286910 µs (97.5%)
                   开销使用:13.008428 ns

在 60 个样本中发现了 1 个异常值(1.6667 %)
    low-severe   1 (1.6667 %)
 异常值方差:7.8228 % 异常值略微增加了方差

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Testing threaded-shuffling ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Evaluation count : 420 in 60 samples of 7 calls.
             执行时间平均值:152.471310 ms
    执行时间标准偏差:8.769236 ms
   执行时间下四分位数:147.954789 ms ( 2.5%)
   执行时间上四分位数:161.277200 ms (97.5%)
                   开销使用:13.008428 ns

Found 3 outliers in 60 samples (5.0000 %)
    low-severe   3 (5.0000 %)
 异常值方差:43.4058 % 异常值适度增加了方差

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 测试线程局部洗牌 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
评估次数:960 次,来自 16 次调用中的 60 个样本。
             执行时间平均值:64.462853 ms
    执行时间标准偏差:1.407808 ms
   执行时间下四分位数:62.353265 ms ( 2.5%)
   执行时间上四分位数:67.197368 ms (97.5%)
                   开销使用:13.008428 ns

在 60 个样本中发现了 1 个异常值(1.6667 %)
    low-severe   1 (1.6667 %)
 异常值方差:9.4540 % 异常值略微增加了方差
0
by

评论者:gfredericks

我认为使用 {{ThreadLocal}} 与添加 } 的逻辑上是独立的,因此它可以是一个独立的条目。我只是在这里提到了它,因为它可以减轻某些用例中 } 造成的任何缓慢,但现在我再看看基准结果,这种缓慢可能并不重要。

0
by

评论者:gfredericks

另外,值得注意的是,正如我在基准代码中所做的那样,仅通过补丁的更改(即没有 ThreadLocal 参与),用户仍然可以手动执行 ThreadLocal,这是目前不可能的。

0
by

评论者: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](https://clojure.atlassian.net/browse/CLJ-1452)(由gfredericks报告)
...