From ebe1435fbb50bb82bfca53063e8daba4f870ec8d Mon Sep 17 00:00:00 2001 From: zhangyunhao Date: Mon, 18 Apr 2022 15:23:20 +0800 Subject: [PATCH] runtime: add fastrand64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support fastrand64 in the runtime, although fastrand uses wyrand to generate 64-bit random number, it still returns uint32. In some cases, we need to generate a 64-bit random number, the new API would be faster and easier to use, and at least we can use the new function in these places: src/net/dnsclient.go:randInt() src/hash/maphash/maphash.go:MakeSeed() src/runtime/map.go:mapiterinit() name time/op Fastrand-16 0.09ns ± 5% Fastrand64-16 0.09ns ± 6% Change-Id: Ibb97378c7ca59bc7dc15535d4872fa58ea112e6a Reviewed-on: https://go-review.googlesource.com/c/go/+/400734 Reviewed-by: Keith Randall Run-TryBot: Keith Randall Auto-Submit: Keith Randall Reviewed-by: Keith Randall TryBot-Result: Gopher Robot Reviewed-by: Ian Lance Taylor --- src/runtime/export_test.go | 1 + src/runtime/rand_test.go | 8 ++++++++ src/runtime/stubs.go | 39 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go index af27050bfd0..8a81f42ca09 100644 --- a/src/runtime/export_test.go +++ b/src/runtime/export_test.go @@ -277,6 +277,7 @@ func CountPagesInUse() (pagesInUse, counted uintptr) { } func Fastrand() uint32 { return fastrand() } +func Fastrand64() uint64 { return fastrand64() } func Fastrandn(n uint32) uint32 { return fastrandn(n) } type ProfBuf profBuf diff --git a/src/runtime/rand_test.go b/src/runtime/rand_test.go index 1b84c79d24f..92d07ebadac 100644 --- a/src/runtime/rand_test.go +++ b/src/runtime/rand_test.go @@ -18,6 +18,14 @@ func BenchmarkFastrand(b *testing.B) { }) } +func BenchmarkFastrand64(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + Fastrand64() + } + }) +} + func BenchmarkFastrandHashiter(b *testing.B) { var m = make(map[int]int, 10) for i := 0; i < 10; i++ { diff --git a/src/runtime/stubs.go b/src/runtime/stubs.go index 8c4ab3ed4e6..ca0cd1ba25e 100644 --- a/src/runtime/stubs.go +++ b/src/runtime/stubs.go @@ -157,6 +157,45 @@ func fastrandn(n uint32) uint32 { return uint32(uint64(fastrand()) * uint64(n) >> 32) } +func fastrand64() uint64 { + mp := getg().m + // Implement wyrand: https://github.com/wangyi-fudan/wyhash + // Only the platform that math.Mul64 can be lowered + // by the compiler should be in this list. + if goarch.IsAmd64|goarch.IsArm64|goarch.IsPpc64| + goarch.IsPpc64le|goarch.IsMips64|goarch.IsMips64le| + goarch.IsS390x|goarch.IsRiscv64 == 1 { + mp.fastrand += 0xa0761d6478bd642f + hi, lo := math.Mul64(mp.fastrand, mp.fastrand^0xe7037ed1a0b428db) + return hi ^ lo + } + + // Implement xorshift64+: 2 32-bit xorshift sequences added together. + // Xorshift paper: https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf + // This generator passes the SmallCrush suite, part of TestU01 framework: + // http://simul.iro.umontreal.ca/testu01/tu01.html + t := (*[2]uint32)(unsafe.Pointer(&mp.fastrand)) + s1, s0 := t[0], t[1] + s1 ^= s1 << 17 + s1 = s1 ^ s0 ^ s1>>7 ^ s0>>16 + r := uint64(s0 + s1) + + s0, s1 = s1, s0 + s1 ^= s1 << 17 + s1 = s1 ^ s0 ^ s1>>7 ^ s0>>16 + r += uint64(s0+s1) << 32 + + t[0], t[1] = s0, s1 + return r +} + +func fastrandu() uint { + if goarch.PtrSize == 4 { + return uint(fastrand()) + } + return uint(fastrand64()) +} + //go:linkname sync_fastrandn sync.fastrandn func sync_fastrandn(n uint32) uint32 { return fastrandn(n) }