英文:
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
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论