2024 年 Clojure 状况调查!分享您的想法。

欢迎!请查看关于页面了解更多关于该功能的信息。

+9
Java 互操作
Clojure 的随机函数目前使用 {{Math.random}} 以及相关的功能,这使得它们无法进行种子。使用动态 var(与额外参数相比)似乎是一个合适的用法,因为希望表现出随机行为的库代码可以不用任何额外的工作透明地支持 seeds。

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

我认为这从语义上讲不会是一个破坏性变更。

### Criterium 基准测试

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

使用的代码位于 [https://github.com/gfredericks/clj-1452-tests]; 原始输出位于注释中。

{{rand}} 稍微慢一些,而 {{shuffle}} 签的正常速度更快。使用来自 8 个线程的 {{shuffle}} 稍慢,但在修补版本中将 {{ThreadLocalRandom}} 手动更改为后,结果是速度提升了 2.5 倍。

在我的 8 核心 Linode VM 上运行

||基准||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

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

如果描述中用表格总结了测试结果,并且将标准输出放在注释中会更好。

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%)
    低严重   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 % 异常值略微影响了方差

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 测试-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 纳秒
    执行时间标准偏差: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

评论由: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 方面,不再是问题,对吗?

0

评论者:alexmiller

正确。

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