英文:
How to rewrite rand.Seed(1) when migrate from go 1.13 to 1.20.1?
问题
文件 bench.go:
package bench
import "os"
func FileLen(f string, bufsize int) (int, error) {
file, err := os.Open(f)
if err != nil {
return 0, err
}
defer file.Close()
count := 0
buf := make([]byte, bufsize)
for {
num, err := file.Read(buf)
count += num
if err != nil {
break
}
}
return count, nil
}
文件 bench_test.go:
package bench
import (
"fmt"
"math/rand"
"os"
"testing"
)
func TestMain(m *testing.M) {
makeData()
exitVal := m.Run()
os.Remove("testdata/data.txt")
os.Exit(exitVal)
}
// makeData makes our data file for us. Rather than checking in a large file, we recreate it for the test.
// By setting the random seed to the same value every time, we ensure that we generate the same file every time.
// This random seed generates a file that's 65,204 bytes long.
func makeData() {
file, err := os.Create("testdata/data.txt")
if err != nil {
panic(err)
}
defer file.Close()
rand.Seed(1)
for i := 0; i < 10000; i++ {
data := makeWord(rand.Intn(10) + 1)
file.Write(data)
}
}
func makeWord(l int) []byte {
out := make([]byte, l+1)
for i := 0; i < l; i++ {
out[i] = 'a' + byte(rand.Intn(26))
}
out[l] = '\n'
return out
}
func TestFileLen(t *testing.T) {
result, err := FileLen("testdata/data.txt", 1)
if err != nil {
t.Fatal(err)
}
if result != 65204 {
t.Error("Expected 65204, got", result)
}
}
var blackhole int
func BenchmarkFileLen1(b *testing.B) {
for i := 0; i < b.N; i++ {
result, err := FileLen("testdata/data.txt", 1)
if err != nil {
b.Fatal(err)
}
blackhole = result
}
}
func BenchmarkFileLen(b *testing.B) {
for _, v := range []int{1, 10, 100, 1000, 10000, 100000} {
b.Run(fmt.Sprintf("FileLen-%d", v), func(b *testing.B) {
for i := 0; i < b.N; i++ {
result, err := FileLen("testdata/data.txt", v)
if err != nil {
b.Fatal(err)
}
blackhole = result
}
})
}
}
https://github.com/learning-go-book-2evy/test_examples/blob/master/bench/bench_test.go#L27
我尝试将 rand.Seed(1) 更改为 rand.NewSource(1),但测试失败了。在从 Go 1.13 迁移到 1.20.1 时如何重写 rand.Seed(1)?
英文:
File bench.go
package bench
import "os"
func FileLen(f string, bufsize int) (int, error) {
file, err := os.Open(f)
if err != nil {
return 0, err
}
defer file.Close()
count := 0
buf := make([]byte, bufsize)
for {
num, err := file.Read(buf)
count += num
if err != nil {
break
}
}
return count, nil
}
File bench_test.go
package bench
import (
"fmt"
"math/rand"
"os"
"testing"
)
func TestMain(m *testing.M) {
makeData()
exitVal := m.Run()
os.Remove("testdata/data.txt")
os.Exit(exitVal)
}
// makeData makes our data file for us. Rather than checking in a large file, we recreate it for the test.
// By setting the random seed to the same value every time, we ensure that we generate the same file every time.
// This random seed generates a file that's 65,204 bytes long.
func makeData() {
file, err := os.Create("testdata/data.txt")
if err != nil {
panic(err)
}
defer file.Close()
rand.Seed(1)
for i := 0; i < 10000; i++ {
data := makeWord(rand.Intn(10) + 1)
file.Write(data)
}
}
func makeWord(l int) []byte {
out := make([]byte, l+1)
for i := 0; i < l; i++ {
out[i] = 'a' + byte(rand.Intn(26))
}
out[l] = '\n'
return out
}
func TestFileLen(t *testing.T) {
result, err := FileLen("testdata/data.txt", 1)
if err != nil {
t.Fatal(err)
}
if result != 65204 {
t.Error("Expected 65204, got", result)
}
}
var blackhole int
func BenchmarkFileLen1(b *testing.B) {
for i := 0; i < b.N; i++ {
result, err := FileLen("testdata/data.txt", 1)
if err != nil {
b.Fatal(err)
}
blackhole = result
}
}
func BenchmarkFileLen(b *testing.B) {
for _, v := range []int{1, 10, 100, 1000, 10000, 100000} {
b.Run(fmt.Sprintf("FileLen-%d", v), func(b *testing.B) {
for i := 0; i < b.N; i++ {
result, err := FileLen("testdata/data.txt", v)
if err != nil {
b.Fatal(err)
}
blackhole = result
}
})
}
}
https://github.com/learning-go-book-2evy/test_examples/blob/master/bench/bench_test.go#L27
I tried change to rand.NewSource(1) but test fail. How to rewrite rand.Seed(1) when migrate from go 1.13 to 1.20.1?
答案1
得分: 3
截至Go 1.20.1,rand.Rand文档中存在一个错误。它写道:
> 需要特定结果序列的程序应使用NewRand(NewSource(seed))。
PRNG的构造函数应该是New,而不是NewRand。
要替换全局种子的使用,将rand.NewSource的返回值传递给rand.New以获取一个PRNG。然后,该PRNG具有与包级函数相同的API:
package main
import "math/rand"
func main() {
rng := rand.New(rand.NewSource(1))
n := rng.Intn(10) + 1 // 替换rand.Intn
_ = n
}
将原始代码更改为以下内容将通过基准测试:
func makeData() {
file, err := os.Create("testdata/data.txt")
if err != nil {
panic(err)
}
defer file.Close()
r := rand.New(rand.NewSource(1))
for i := 0; i < 10000; i++ {
data := makeWord(r.Intn(10)+1, r)
file.Write(data)
}
}
func makeWord(l int, r *rand.Rand) []byte {
out := make([]byte, l+1)
for i := 0; i < l; i++ {
out[i] = 'a' + byte(r.Intn(26))
}
out[l] = '\n'
return out
}
英文:
As of Go 1.20.1 there is an error in the documentation for rand.Rand. It reads
> Programs that need a specific result sequence should use NewRand(NewSource(seed)).
The constructor for the PRNG is called New, not NewRand.
To replace uses of the global seed, pass the return value of rand.NewSource to rand.New to obtain a PRNG. This PRNG then has the same API as the package level functions:
package main
import "math/rand"
func main() {
rng := rand.New(rand.NewSource(1))
n := rng.Intn(10) + 1 // instead of rand.Intn
_ = n
}
Changing the original to the following will pass the benchmark tests:
func makeData() {
file, err := os.Create("testdata/data.txt")
if err != nil {
panic(err)
}
defer file.Close()
r := rand.New(rand.NewSource(1))
for i := 0; i < 10000; i++ {
data := makeWord(r.Intn(10)+1, r)
file.Write(data)
}
}
func makeWord(l int, r *rand.Rand) []byte {
out := make([]byte, l+1)
for i := 0; i < l; i++ {
out[i] = 'a' + byte(r.Intn(26))
}
out[l] = '\n'
return out
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。



评论