go - 参考时间 % 12345 的“不稳定值”
问题描述
在这个反射器包中,它提到了一个不稳定的值被用作名称后缀。这是以 12345 为模的纳秒数。这是有意义的还是只是伪随机的同义词,所以人类不会解释它?
// reflectorDisambiguator is used to disambiguate started reflectors.
// initialized to an unstable value to ensure meaning isn't attributed to the suffix.
var reflectorDisambiguator = int64(time.Now().UnixNano() % 12345)
不稳定这个词特别让我不确定。在这种情况下是什么意思?
与另一种获取 12345 以下的随机数的方法相比,这样做是否有优势?
解决方案
意思似乎很清楚:
Kubernetes 提交:1da4f4a745bf536c34e377321a252b4774d1a7e0
工具/缓存/reflector.go
// reflectorDisambiguator is used to disambiguate started reflectors. // initialized to an unstable value to ensure meaning isn't attributed to the suffix.
后缀行为不应该是确定性的,因为您不应该依赖特定的实现行为。
例如,围棋地图也出现了类似的情况:
对于带有范围子句的语句
未指定映射的迭代顺序,并且不保证从一次迭代到下一次迭代是相同的。
在地图中迭代
旧的语言规范没有定义映射的迭代顺序,实际上它在硬件平台上有所不同。这导致迭代地图的测试变得脆弱且不可移植,具有令人不快的特性,即测试可能总是在一台机器上通过但在另一台机器上中断。
在 Go 1 中,使用 for range 语句在映射上迭代时访问元素的顺序被定义为不可预测,即使同一个循环使用同一个映射多次运行。代码不应假定元素以任何特定顺序被访问。
这种变化意味着依赖于迭代顺序的代码很可能会提前中断并在它成为问题之前很久就被修复。同样重要的是,即使程序使用范围循环从地图中选择元素,它也允许地图实现确保更好的地图平衡。
地图迭代
小地图上的迭代不再以一致的顺序发生。Go 1 定义“未指定映射的迭代顺序,也不保证从一次迭代到下一次迭代的顺序相同。” 为了避免代码依赖于地图迭代顺序,Go 1.0 从地图中的随机索引开始每次地图迭代。Go 1.1 中引入的新地图实现忽略了对具有八个或更少条目的地图进行随机迭代,尽管迭代顺序仍可能因系统而异。这使得人们可以编写依赖于小地图迭代顺序的 Go 1.1 和 Go 1.2 程序,因此只能在某些系统上可靠地工作。Go 1.3 重新引入了小地图的随机迭代,以消除这些错误。
更新:如果代码假设小地图的迭代顺序是固定的,它将中断并且必须重写以不做出该假设。因为只有小地图受到影响,所以问题最常出现在测试中。
类似的担忧导致了一个未实施的提案,以确保不稳定排序的顺序不稳定:
疯狂的想法,但是如果 sort.Sort 在开始之前随机排列它的输入怎么办?
Go 1.6 的 sort.Sort 与 Go 1.5 不同,我在 Google 看到至少有十几个测试失败,这些失败隐含地依赖于旧算法。通常的情况是您按结构中的一个字段对结构切片进行排序。如果存在该字段相等但其他不相等的条目,并且您希望最后的结构具有特定顺序,则取决于 sort.Sort 的算法。稍后的 sort.Sort 可能会做出不同的选择并产生不同的顺序。这使得程序不能从一个版本的 Go 移植到另一个版本,就像映射哈希差异用于使程序不能从一个架构移植到另一个架构一样。我们通过稍微随机化迭代顺序来解决地图问题。在地图的情况下,它不是一个完整的排列,而是足以使测试明显不稳定的变化。
我想知道我们是否应该对 sort.Sort 做同样的事情。只需要 N 次交换就能很好地洗牌,而且我们已经使用了 Nlog(N) 次交换,所以 N(log(N)+1) 不太可能被注意到。这也可以防止恶意输入。
这会让人们感到惊讶,尤其是那些认为 sort.Sort == sort.Stable 的人。但理由是,最好在他们第一次运行代码时给他们一个惊喜,而不是在以后发布许多 Go 版本时给他们惊喜。
以下是比较的time.Now()
基准rand.Intn()
:
package main
import "testing"
import (
rand "math/rand"
"time"
)
// https://github.com/kubernetes/client-go/blob/79cb21f5b3b1dd8f8b23bd3f79925b4fda4e2562/tools/cache/reflector.go#L100
var reflectorDisambiguator = int64(time.Now().UnixNano() % 12345)
func BenchmarkTimeNow(b *testing.B) {
for N := 0; N < b.N; N++ {
reflectorDisambiguator = int64(time.Now().UnixNano() % 12345)
}
}
// rand.Intn()
func init() {
rand.Seed(time.Now().UnixNano())
reflectorDisambiguator = int64(rand.Intn(12345))
}
func BenchmarkRandIntn(b *testing.B) {
for N := 0; N < b.N; N++ {
rand.Seed(time.Now().UnixNano())
reflectorDisambiguator = int64(rand.Intn(12345))
}
}
输出:
$ go test disambiguator_test.go -bench=.
goos: linux
goarch: amd64
BenchmarkTimeNow-4 20000000 67.5 ns/op
BenchmarkRandIntn-4 100000 11941 ns/op
$
推荐阅读
- c# - 使用位置记录构造函数和/或初始化属性时如何检查参数是否为空?
- node.js - 为什么节点 14 和 16 的 AzureDevOps 私有 NPM 存储库如此缓慢?
- python - 将一个列表中的值(在 dict 中)与另一个列表进行比较 [Python]
- linux - 如何在 UNIX/LINUX 中特定行的末尾添加字符?
- c# - 无法使用凭证从 C# 控制台应用程序连接到 IBM MQ
- javascript - 推杆在 30 秒后断开
- css - prefers-color-scheme:dark 不会从 SCSS 编译为 CSS 样式表
- haskell - 我可以教 GHC 数学归纳法吗?
- php - Wordpress 中 CPT 的分层分类和永久链接返回 404
- r - 带有ggplot的for循环-rep_len(TRUE,nrow(x))中的错误:无效的'length.out'值