Perf: avoid using rand.Rand casting

This commit is contained in:
kayos@tcp.direct 2023-11-10 16:25:17 -08:00
parent 19fdd70713
commit 4b9164bd75
Signed by: kayos
GPG Key ID: 4B841471B4BEE979
2 changed files with 38 additions and 18 deletions

View File

@ -4,8 +4,11 @@ import (
crip "crypto/rand"
"encoding/binary"
"math/rand"
"os"
"reflect"
"sync"
"time"
"unsafe"
"nullprogram.com/x/rng"
@ -16,11 +19,11 @@ type randPool struct {
sync.Pool
}
func (p *randPool) Get() *rand.Rand {
return p.Pool.Get().(*rand.Rand)
func (p *randPool) Get() *rng.SplitMix64 {
return p.Pool.Get().(*rng.SplitMix64)
}
func (p *randPool) Put(r *rand.Rand) {
func (p *randPool) Put(r *rng.SplitMix64) {
p.Pool.Put(r)
}
@ -30,8 +33,7 @@ var (
New: func() interface{} {
sm64 := new(rng.SplitMix64)
sm64.Seed(GetCryptoSeed())
prng := rand.New(sm64) //nolint:gosec
return prng
return sm64
},
},
}
@ -42,16 +44,18 @@ var (
func setSharedRand() {
hardLocc.Lock()
sharedRand = lolXD.Get()
sharedRand = rand.New(lolXD.Get())
hardLocc.Unlock()
}
func AcquireRand() *rand.Rand {
return lolXD.Get()
return rand.New(lolXD.Get())
}
func ReleaseRand(r *rand.Rand) {
lolXD.Put(r)
srcField := reflect.ValueOf(r).Elem().FieldByName("src")
src := reflect.NewAt(srcField.Type(), unsafe.Pointer(srcField.UnsafeAddr())).Elem().Interface().(*rng.SplitMix64)
lolXD.Put(src)
r = nil
}
@ -80,18 +84,17 @@ func GetOptimizedRand() *rand.Rand {
}
// GetSharedRand returns a pointer to our shared optimized rand.Rand which uses crypto/rand to seed a splitmix64 rng.
// WARNING - RACY - This is not thread safe, and should only be used in a single-threaded context.
func GetSharedRand() *rand.Rand {
getSharedRand.Do(func() {
setSharedRand()
})
return sharedRand
return rand.New(sharedRand)
}
// RNGUint32 returns a random uint32 using crypto/rand and splitmix64.
func RNGUint32() uint32 {
r := lolXD.Get()
ui := r.Uint32()
ui := uint32(r.Int63() >> 31)
lolXD.Put(r)
return ui
}
@ -106,8 +109,25 @@ RNG returns integer with a maximum amount of 'n' using a global/shared instance
*/
func RNG(n int) int {
r := lolXD.Get()
i := r.Intn(n)
lolXD.Put(r)
defer lolXD.Put(r)
i := 0
if n <= 0 {
// because panic is just rude.
_, _ = os.Stderr.WriteString("RNG: n must be greater than 0, returning 0")
return i
}
if n <= 1<<31-1 {
n32 := int32(n)
if n32&(n32-1) == 0 {
i = int(int32(r.Int63()>>32) & (n32 - 1))
}
maximum := int32((1 << 31) - 1 - (1<<31)%uint32(n32))
v := int32(r.Int63() >> 32)
for v > maximum {
v = int32(r.Int63() >> 32)
}
i = int(v % n32)
}
return i
}
@ -167,7 +187,7 @@ func randStr(upper bool, size int) string {
buf := strBufs.Get()
r := lolXD.Get()
for i := 0; i != size; i++ {
ui32 := int(r.Uint32())
ui32 := int(r.Int63() >> 31)
switch upper {
case true:
_ = buf.WriteByte(charsetWithUpper[ui32%len(charsetWithUpper)])

View File

@ -49,15 +49,15 @@ func Test_RNG(t *testing.T) {
t.Errorf("GetSharedRand(55555) returned the same value twice!")
}
r := AcquireRand()
one := r.Intn(55555)
two := r.Intn(55555)
one := r.Uint64()
two := r.Uint64()
if one == two {
t.Errorf("AcquireRand() returned the same value twice!")
}
ReleaseRand(r)
r = AcquireRand()
one1 := r.Intn(55555)
two1 := r.Intn(55555)
one1 := r.Uint64()
two1 := r.Uint64()
if one1 == two1 {
t.Errorf("AcquireRand() returned the same value twice!")
}