From 30f94d6eae4e10ad012741511b4bbf7cf308b8b7 Mon Sep 17 00:00:00 2001 From: "kayos@tcp.direct" Date: Tue, 12 Jul 2022 02:00:54 -0700 Subject: [PATCH] Enhancement: shared/global rand objs + add benchmarks --- entropy/entropy.go | 30 +++++++++++++++++++++------ entropy/entropy_test.go | 46 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 66 insertions(+), 10 deletions(-) diff --git a/entropy/entropy.go b/entropy/entropy.go index fe45954..b188d16 100644 --- a/entropy/entropy.go +++ b/entropy/entropy.go @@ -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). diff --git a/entropy/entropy_test.go b/entropy/entropy_test.go index f3e0372..240bf8d 100644 --- a/entropy/entropy_test.go +++ b/entropy/entropy_test.go @@ -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) + } +}