diff --git a/rand/rand.go b/rand/rand.go index 5166b94..61587df 100644 --- a/rand/rand.go +++ b/rand/rand.go @@ -3,11 +3,8 @@ package rand import ( "crypto/rand" "io" - "sync" - "time" "math/big" - mrnd "math/rand" "github.com/charlienet/go-mixed/bytesconv" ) @@ -23,36 +20,33 @@ const ( _ = allChars + "/+" ) -var ( - randSource mrnd.Source = mrnd.NewSource(time.Now().UnixNano()) - randLock sync.Mutex -) - -func init() { - mrnd.Seed(time.Now().UnixNano()) -} +var rng = NewRandGenerator() type charScope struct { - bytes []byte - length int - max int - bits int - mask int - lenFunc func(int) int + bytes []byte + length int + max int + bits int + mask int } +var ( + Uppercase = StringScope(uppercase) // 大写字母 + Lowercase = StringScope(lowercase) // 小写字母 + Digit = StringScope(digit) // 数字 + Nomix = StringScope(nomix) // 不混淆字符 + Letter = StringScope(letter) // 字母 + Hex = StringScope(hex) // 十六进制字符 + AllChars = StringScope(allChars) // 所有字符 +) + func StringScope(str string) *charScope { - return strScope(str, nil) -} - -func strScope(str string, f func(int) int) *charScope { len := len(str) scope := &charScope{ - bytes: bytesconv.StringToBytes(str), - length: len, - lenFunc: f, - bits: 1, + bytes: bytesconv.StringToBytes(str), + length: len, + bits: 1, } for scope.mask < len { @@ -65,27 +59,14 @@ func strScope(str string, f func(int) int) *charScope { return scope } -var ( - Uppercase = StringScope(uppercase) // 大写字母 - Lowercase = StringScope(lowercase) // 小写字母 - Digit = StringScope(digit) // 数字 - Nomix = StringScope(nomix) // 不混淆字符 - Letter = StringScope(letter) // 字母 - Hex = strScope(hex, func(n int) int { return n * 2 }) // 十六进制字符 - AllChars = StringScope(allChars) // 所有字符 -) - // 生成指定长度的随机字符串 func (scope *charScope) Generate(length int) string { n := length - if scope.lenFunc != nil { - n = scope.lenFunc(n) - } - ret := make([]byte, n) - for i, cache, remain := n-1, randInt63(), scope.max; i >= 0; { + + for i, cache, remain := n-1, rng.Int63(), scope.max; i >= 0; { if remain == 0 { - cache, remain = randInt63(), scope.max + cache, remain = rng.Int63(), scope.max } if idx := int(cache & int64(scope.mask)); idx < scope.length { @@ -106,7 +87,7 @@ type scopeConstraint interface { // 生成区间 n >= 0, n < max func Intn[T scopeConstraint](max T) T { - n := mrnd.Int63n(int64(max)) + n := rng.Int63n(int64(max)) return T(n) % max } @@ -133,17 +114,3 @@ func RandBytes(len int) ([]byte, error) { _, err := io.ReadFull(rand.Reader, r) return r, err } - -func randInt63() int64 { - var v int64 - - randLock.Lock() - v = randSource.Int63() - randLock.Unlock() - - return v -} - -func randNumber2(max int) int { - return mrnd.Intn(max) -} diff --git a/rand/rand_generator.go b/rand/rand_generator.go new file mode 100644 index 0000000..2544bc7 --- /dev/null +++ b/rand/rand_generator.go @@ -0,0 +1,102 @@ +package rand + +import ( + mrnd "math/rand" + "runtime" + "sync/atomic" + "time" +) + +var ( + seed = time.Now().UnixNano() // 随机数种子 + souce = mrnd.NewSource(time.Now().UnixNano()) // 用于初始化的随机数生成器 +) + +type randGenerator struct { + source mrnd.Source + r uint32 +} + +func NewRandGenerator() *randGenerator { + return &randGenerator{ + source: mrnd.NewSource(getSeed()), + } +} + +func getSeed() int64 { + seed ^= souce.Int63() + return seed +} + +// Int63n returns, as an int64, a non-negative pseudo-random number in the half-open interval [0,n). +// It panics if n <= 0. +func (r *randGenerator) Int63n(n int64) int64 { + if n <= 0 { + panic("invalid argument to Int63n") + } + if n&(n-1) == 0 { // n is power of two, can mask + return r.Int63() & (n - 1) + } + max := int64((1 << 63) - 1 - (1<<63)%uint64(n)) + v := r.Int63() + for v > max { + v = r.Int63() + } + + return v % n +} + +func (r *randGenerator) Int31() int32 { + return int32(r.Int63() >> 32) +} + +func (r *randGenerator) Int31n(n int32) int32 { + if n <= 0 { + panic("invalid argument to Int31n") + } + + if n&(n-1) == 0 { // n is power of two, can mask + return r.Int31() & (n - 1) + } + max := int32((1 << 31) - 1 - (1<<31)%uint32(n)) + v := r.Int31() + for v > max { + v = r.Int31() + } + return v % n +} + +func (r *randGenerator) Int() int { + u := uint(r.Int63()) + return int(u << 1 >> 1) // clear sign bit if int == int32 +} + +// Intn returns, as an int, a non-negative pseudo-random number in the half-open interval [0,n). +// It panics if n <= 0. +func (r *randGenerator) Intn(n int) int { + if n <= 0 { + panic("invalid argument to Intn") + } + if n <= 1<<31-1 { + return int(r.Int31n(int32(n))) + } + return int(r.Int63n(int64(n))) +} + +func (r *randGenerator) Int63() int64 { + r.lock() + i := r.source.Int63() + r.unlock() + + return i +} + +func (g *randGenerator) lock() { + for !atomic.CompareAndSwapUint32(&g.r, 0, 1) { + runtime.Gosched() + } +} + +func (g *randGenerator) unlock() { + atomic.StoreUint32(&g.r, 0) +} diff --git a/rand/rand_test.go b/rand/rand_test.go index 2e2a9fe..111a63f 100644 --- a/rand/rand_test.go +++ b/rand/rand_test.go @@ -56,6 +56,27 @@ func TestRange(t *testing.T) { } } +func TestGenerator(t *testing.T) { + g := rand.NewRandGenerator() + + for i := 0; i < 100; i++ { + t.Log(g.Int63()) + } +} + +func TestMutiGenerator(t *testing.T) { + set := make(map[int64]struct{}, 1000) + + for i := 0; i < 1000; i++ { + r := rand.NewRandGenerator().Int63() + if _, ok := set[r]; ok { + t.Fatal("生成的随机数重复") + } + + set[r] = struct{}{} + } +} + func BenchmarkParallel(b *testing.B) { rand.Hex.Generate(16) @@ -72,15 +93,46 @@ func BenchmarkNoop(b *testing.B) { } } -func BenchmarkRandString(b *testing.B) { +func BenchmarkHexString(b *testing.B) { + for i := 0; i < b.N; i++ { + rand.RandBytes(16) + } +} - for i := 0; i < 10; i++ { - rand.Hex.Generate(10) +func BenchmarkHexParallel(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + rand.RandBytes(16) + } + }) +} + +func BenchmarkGenerator(b *testing.B) { + r := rand.NewRandGenerator() + for i := 0; i < b.N; i++ { + r.Int63() + } +} + +func BenchmarkParallelGenerator(b *testing.B) { + r := rand.NewRandGenerator() + for i:=0; i<10; i++{ + go r.Int63() } + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + r.Int63() + } + }) +} + +func BenchmarkRandString(b *testing.B) { + b.Log(rand.Hex.Generate(16)) b.ResetTimer() for i := 0; i < b.N; i++ { - rand.Hex.Generate(20) + rand.Hex.Generate(16) } }