2024 年 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
by

评论者:gfredericks

当我们在乱七八糟地修改这些东西时,我们应该尝试将Clojure的随机函数设置为线程局部默认值吗?我们可以创建一个包含{{ThreadLocal}}逻辑的自定义{{Random}}子类,将其作为*的默认值。

0
by

评论者:alexmiller

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

如果描述能够以表格形式总结测试结果,并将标准输出放在注释中会很好。

0
by
_评论者: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 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 ;;
;;;;;;;;;;;;;;;;;;;;;
评估次数:在79299次调用中的60个样本,共4757940次。
             执行时间平均值: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% 离群值使得方差适度膨胀

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 测试threaded-local-shuffling ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
评估次数:在16次调用中的60个样本,共960次。
             执行时间平均值: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

解决方案:(链接:https://github.com/clojure/data generators 文本:data generators)提供了可种子的随机数

0
by

评论者为:gshayban

只是提醒,ThreadLocalRandom是从JDK 7开始有的。

0

评论者:gfredericks

ThreadLocalRandom的JDK7及以上版本不再有问题,对吗?

0

评论者:alexmiller

是的

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