Skip to content

Commit

Permalink
Merge 'concurrency: goroutine local rng' from Henrik
Browse files Browse the repository at this point in the history
"Each PartitionRange now holds an instance of *rand.Rand which
 allows each job to generate random numbers without contending
 for the global lock.

 Fixes: #61"

* origin/goroutine_local_rng:
  concurrency: goroutine local rng
  • Loading branch information
penberg committed Apr 11, 2019
2 parents 050cebe + a0e552b commit b99d033
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 109 deletions.
8 changes: 6 additions & 2 deletions cmd/gemini/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,11 @@ func runJob(f testJob, schema *gemini.Schema, s *gemini.Session, mode string) {

for _, table := range schema.Tables {
for i := 0; i < concurrency; i++ {
p := gemini.PartitionRange{Min: minRange + i*maxRange, Max: maxRange + i*maxRange}
p := gemini.PartitionRange{
Min: minRange + i*maxRange,
Max: maxRange + i*maxRange,
Rand: rand.New(rand.NewSource(int64(seed))),
}
go f(workerCtx, &workers, schema, table, s, p, c, mode)
}
}
Expand Down Expand Up @@ -266,7 +270,7 @@ func Job(ctx context.Context, wg *sync.WaitGroup, schema *gemini.Schema, table g
case readMode:
validationJob(schema, table, s, p, &testStatus)
default:
ind := rand.Intn(100000) % 2
ind := p.Rand.Intn(100000) % 2
if ind == 0 {
mutationJob(schema, table, s, p, &testStatus)
} else {
Expand Down
68 changes: 32 additions & 36 deletions datautils.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,55 +11,51 @@ import (
"github.com/segmentio/ksuid"
)

func randIntRange(min int, max int) int {
return rand.Intn(max-min) + min
func randIntRange(rnd *rand.Rand, min int, max int) int {
return rnd.Intn(max-min) + min
}

func nonEmptyRandIntRange(min int, max int, def int) int {
func nonEmptyRandIntRange(rnd *rand.Rand, min int, max int, def int) int {
if max > min && min > 0 {
return randIntRange(min, max)
return randIntRange(rnd, min, max)
}
return randIntRange(1, def)
return randIntRange(rnd, 1, def)
}

func randInt64Range(min int64, max int64) int64 {
return rand.Int63n(max-min) + min
func randInt64Range(rnd *rand.Rand, min int64, max int64) int64 {
return rnd.Int63n(max-min) + min
}

func nonEmptyRandInt64Range(min int64, max int64, def int64) int64 {
func nonEmptyRandInt64Range(rnd *rand.Rand, min int64, max int64, def int64) int64 {
if max > min && min > 0 {
return randInt64Range(min, max)
return randInt64Range(rnd, min, max)
}
return randInt64Range(1, def)
return randInt64Range(rnd, 1, def)
}

func randFloat32Range(min float32, max float32) float32 {
return rand.Float32() * (max - min)
func randFloat32Range(rnd *rand.Rand, min float32, max float32) float32 {
return rnd.Float32() * (max - min)
}

func nonEmptyRandFloat32Range(min float32, max float32, def float32) float32 {
func nonEmptyRandFloat32Range(rnd *rand.Rand, min float32, max float32, def float32) float32 {
if max > min && min > 0 {
return randFloat32Range(min, max)
return randFloat32Range(rnd, min, max)
}
return randFloat32Range(1, def)
return randFloat32Range(rnd, 1, def)
}

func randFloat64Range(min float64, max float64) float64 {
return rand.Float64() * (max - min)
func randFloat64Range(rnd *rand.Rand, min float64, max float64) float64 {
return rnd.Float64() * (max - min)
}

func nonEmptyRandFloat64Range(min float64, max float64, def float64) float64 {
func nonEmptyRandFloat64Range(rnd *rand.Rand, min float64, max float64, def float64) float64 {
if max > min && min > 0 {
return randFloat64Range(min, max)
return randFloat64Range(rnd, min, max)
}
return randFloat64Range(1, def)
return randFloat64Range(rnd, 1, def)
}

func randString(len int) string {
return nonEmptyRandStringWithTime(len, time.Now().UTC())
}

func randStringWithTime(len int, t time.Time) string {
func randStringWithTime(rnd *rand.Rand, len int, t time.Time) string {
id, _ := ksuid.NewRandomWithTime(t)

var buf strings.Builder
Expand All @@ -70,41 +66,41 @@ func randStringWithTime(len int, t time.Time) string {

// Pad some extra random data
buff := make([]byte, len-buf.Len())
rand.Read(buff)
rnd.Read(buff)
buf.WriteString(base64.StdEncoding.EncodeToString(buff))

return buf.String()[:len]
}

func nonEmptyRandStringWithTime(len int, t time.Time) string {
func nonEmptyRandStringWithTime(rnd *rand.Rand, len int, t time.Time) string {
if len <= 0 {
len = 1
}
return randStringWithTime(len, t)
return randStringWithTime(rnd, len, t)
}

func randDate() string {
time := randTime()
func randDate(rnd *rand.Rand) string {
time := randTime(rnd)
return time.Format("2006-01-02")
}

func randTime() time.Time {
func randTime(rnd *rand.Rand) time.Time {
min := time.Date(1970, 1, 0, 0, 0, 0, 0, time.UTC).Unix()
max := time.Date(2024, 1, 0, 0, 0, 0, 0, time.UTC).Unix()

sec := rand.Int63n(max-min) + min
sec := rnd.Int63n(max-min) + min
return time.Unix(sec, 0)
}

func randTimeNewer(d time.Time) time.Time {
func randTimeNewer(rnd *rand.Rand, d time.Time) time.Time {
min := time.Date(d.Year()+1, 1, 0, 0, 0, 0, 0, time.UTC).Unix()
max := time.Date(2024, 1, 0, 0, 0, 0, 0, time.UTC).Unix()

sec := rand.Int63n(max-min+1) + min
sec := rnd.Int63n(max-min+1) + min
return time.Unix(sec, 0)
}

func randIpV4Address(v, pos int) string {
func randIpV4Address(rnd *rand.Rand, v, pos int) string {
if pos < 0 || pos > 4 {
panic(fmt.Sprintf("invalid position for the desired value of the IP part %d, 0-3 supported", pos))
}
Expand All @@ -116,7 +112,7 @@ func randIpV4Address(v, pos int) string {
if i == pos {
blocks = append(blocks, strconv.Itoa(v))
} else {
blocks = append(blocks, strconv.Itoa(rand.Intn(255)))
blocks = append(blocks, strconv.Itoa(rnd.Intn(255)))
}
}
return strings.Join(blocks, ".")
Expand Down
21 changes: 12 additions & 9 deletions datautils_test.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package gemini

import (
"math/rand"
"testing"
"testing/quick"
"time"
)

var rnd = rand.New(rand.NewSource(time.Now().UnixNano()))

func TestNonEmptyRandRange(t *testing.T) {
f := func(x, y int) bool {
r := nonEmptyRandIntRange(x, y, 10)
r := nonEmptyRandIntRange(rnd, x, y, 10)
return r > 0
}
if err := quick.Check(f, nil); err != nil {
Expand All @@ -18,7 +21,7 @@ func TestNonEmptyRandRange(t *testing.T) {

func TestNonEmptyRandRange64(t *testing.T) {
f := func(x, y int) bool {
r := nonEmptyRandIntRange(x, y, 10)
r := nonEmptyRandIntRange(rnd, x, y, 10)
return r > 0
}
if err := quick.Check(f, nil); err != nil {
Expand All @@ -28,7 +31,7 @@ func TestNonEmptyRandRange64(t *testing.T) {

func TestNonEmptyRandFloat32Range(t *testing.T) {
f := func(x, y float32) bool {
r := nonEmptyRandFloat32Range(x, y, 10)
r := nonEmptyRandFloat32Range(rnd, x, y, 10)
return r > 0
}
if err := quick.Check(f, nil); err != nil {
Expand All @@ -38,7 +41,7 @@ func TestNonEmptyRandFloat32Range(t *testing.T) {

func TestNonEmptyRandFloat64Range(t *testing.T) {
f := func(x, y float64) bool {
r := nonEmptyRandFloat64Range(x, y, 10)
r := nonEmptyRandFloat64Range(rnd, x, y, 10)
return r > 0
}
if err := quick.Check(f, nil); err != nil {
Expand All @@ -50,7 +53,7 @@ func TestNonEmptyRandString(t *testing.T) {
// TODO: Figure out why this is so horribly slow...
tt := time.Now()
f := func(len int32) bool {
r := nonEmptyRandStringWithTime(int(len), tt)
r := nonEmptyRandStringWithTime(rnd, int(len), tt)
return r != ""
}
cfg := &quick.Config{MaxCount: 10}
Expand All @@ -64,15 +67,15 @@ var bench_r string
func BenchmarkNonEmptyRandStringWithTime(b *testing.B) {
tt := time.Now()
for i := 0; i < b.N; i++ {
bench_r = nonEmptyRandStringWithTime(30, tt)
bench_r = nonEmptyRandStringWithTime(rnd, 30, tt)
}
}

func BenchmarkNonEmptyRandStringWithTimeParallel(b *testing.B) {
tt := time.Now()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
bench_r = nonEmptyRandStringWithTime(30, tt)
bench_r = nonEmptyRandStringWithTime(rnd, 30, tt)
}
})
}
Expand All @@ -81,14 +84,14 @@ var bench_rr int

func BenchmarkNonEmptyRandRange(b *testing.B) {
for i := 0; i < b.N; i++ {
bench_rr = nonEmptyRandIntRange(0, 50, 30)
bench_rr = nonEmptyRandIntRange(rnd, 0, 50, 30)
}
}

var bench_rr64 int64

func BenchmarkNonEmptyRandRange64(b *testing.B) {
for i := 0; i < b.N; i++ {
bench_rr64 = nonEmptyRandInt64Range(0, 50, 30)
bench_rr64 = nonEmptyRandInt64Range(rnd, 0, 50, 30)
}
}
Loading

0 comments on commit b99d033

Please sign in to comment.