Merge branch 'fast-rand'

This commit is contained in:
kayos@tcp.direct 2022-07-12 02:11:20 -07:00
commit ed7837591a
Signed by: kayos
GPG Key ID: 4B841471B4BEE979
2 changed files with 66 additions and 10 deletions

View File

@ -4,11 +4,17 @@ import (
crip "crypto/rand"
"encoding/binary"
"math/rand"
"sync"
"time"
"nullprogram.com/x/rng"
)
var (
sharedRand *rand.Rand
getSharedRand = &sync.Once{}
)
// RandomStrChoice returns a random item from an input slice of strings.
func RandomStrChoice(choice []string) string {
if len(choice) > 0 {
@ -25,7 +31,8 @@ func GetCryptoSeed() int64 {
return seed
}
// GetOptimizedRand returns a pointer to a new rand.Rand which uses crypto/rand to seed a splitmix64 rng.
// GetOptimizedRand returns a pointer to a *new* rand.Rand which uses crypto/rand to seed a splitmix64 rng.
// Does not use the global/shared instance of a splitmix64 rng, but instead creates a new one.
func GetOptimizedRand() *rand.Rand {
r := new(rng.SplitMix64)
r.Seed(GetCryptoSeed())
@ -34,14 +41,25 @@ func GetOptimizedRand() *rand.Rand {
// RNGUint32 returns a random uint32 using crypto/rand and splitmix64.
func RNGUint32() uint32 {
r := GetOptimizedRand()
return r.Uint32()
getSharedRand.Do(func() {
sharedRand = GetOptimizedRand()
})
return sharedRand.Uint32()
}
// RNG returns integer with a maximum amount of 'n' using crypto/rand and splitmix64.
/*RNG returns integer with a maximum amount of 'n' using a global/shared instance of a splitmix64 rng.
- Benchmark_FastRandStr5-24 25205089 47.03 ns/op
- Benchmark_FastRandStr25-24 7113620 169.8 ns/op
- Benchmark_FastRandStr55-24 3520297 340.7 ns/op
- Benchmark_FastRandStr500-24 414966 2837 ns/op
- Benchmark_FastRandStr55555-24 3717 315229 ns/op
*/
func RNG(n int) int {
r := GetOptimizedRand()
return r.Intn(n)
getSharedRand.Do(func() {
sharedRand = GetOptimizedRand()
})
return sharedRand.Intn(n)
}
// OneInA generates a random number with a maximum of 'million' (input int).

View File

@ -2,6 +2,7 @@ package entropy
import (
"strings"
"sync"
"testing"
)
@ -41,10 +42,10 @@ func Test_RandStr(t *testing.T) {
for n := 0; n != 500; n++ {
zero := RandStr(55)
one := RandStr(55)
t.Logf("Random0: %s Random1: %s", zero, one)
// t.Logf("Random0: %s Random1: %s", zero, one)
randStrChecks(zero, one, t)
}
t.Logf("[SUCCESS] RandStr had no collisions")
}
func Test_RandStr_Entropy(t *testing.T) {
@ -67,10 +68,10 @@ func Test_RandStr_Entropy(t *testing.T) {
t.Errorf("[ENTROPY FAILURE] more than a quarter of the string is the same!\n zero: %s \n one: %s \nTotal similar: %d",
zero, one, similarity)
}
t.Logf("[ENTROPY] Similarity score (lower is better): %d", similarity)
// t.Logf("[ENTROPY] Similarity score (lower is better): %d", similarity)
totalScore += similarity
}
t.Logf("[ENTROPY] final score (lower is better): %d", totalScore)
t.Logf("[ENTROPY] final score (lower is better): %d (RandStr)", totalScore)
}
func Test_RandomStrChoice(t *testing.T) {
@ -83,3 +84,40 @@ func Test_RandomStrChoice(t *testing.T) {
}
check(RandomStrChoice(slice), RandomStrChoice(slice), t)
}
func Test_RNGUint32(t *testing.T) {
// start globals fresh, just for coverage.
sharedRand = GetOptimizedRand()
getSharedRand = &sync.Once{}
RNGUint32()
}
func Benchmark_RandStr5(b *testing.B) {
for n := 0; n != b.N; n++ {
RandStr(5)
}
}
func Benchmark_RandStr25(b *testing.B) {
for n := 0; n != b.N; n++ {
RandStr(25)
}
}
func Benchmark_RandStr55(b *testing.B) {
for n := 0; n != b.N; n++ {
RandStr(55)
}
}
func Benchmark_RandStr500(b *testing.B) {
for n := 0; n != b.N; n++ {
RandStr(500)
}
}
func Benchmark_RandStr55555(b *testing.B) {
for n := 0; n != b.N; n++ {
RandStr(55555)
}
}