Skip to content

Commit b99d033

Browse files
committed
Merge 'concurrency: goroutine local rng' from Henrik
"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
2 parents 050cebe + a0e552b commit b99d033

File tree

4 files changed

+113
-109
lines changed

4 files changed

+113
-109
lines changed

cmd/gemini/root.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,11 @@ func runJob(f testJob, schema *gemini.Schema, s *gemini.Session, mode string) {
159159

160160
for _, table := range schema.Tables {
161161
for i := 0; i < concurrency; i++ {
162-
p := gemini.PartitionRange{Min: minRange + i*maxRange, Max: maxRange + i*maxRange}
162+
p := gemini.PartitionRange{
163+
Min: minRange + i*maxRange,
164+
Max: maxRange + i*maxRange,
165+
Rand: rand.New(rand.NewSource(int64(seed))),
166+
}
163167
go f(workerCtx, &workers, schema, table, s, p, c, mode)
164168
}
165169
}
@@ -266,7 +270,7 @@ func Job(ctx context.Context, wg *sync.WaitGroup, schema *gemini.Schema, table g
266270
case readMode:
267271
validationJob(schema, table, s, p, &testStatus)
268272
default:
269-
ind := rand.Intn(100000) % 2
273+
ind := p.Rand.Intn(100000) % 2
270274
if ind == 0 {
271275
mutationJob(schema, table, s, p, &testStatus)
272276
} else {

datautils.go

+32-36
Original file line numberDiff line numberDiff line change
@@ -11,55 +11,51 @@ import (
1111
"github.com/segmentio/ksuid"
1212
)
1313

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

18-
func nonEmptyRandIntRange(min int, max int, def int) int {
18+
func nonEmptyRandIntRange(rnd *rand.Rand, min int, max int, def int) int {
1919
if max > min && min > 0 {
20-
return randIntRange(min, max)
20+
return randIntRange(rnd, min, max)
2121
}
22-
return randIntRange(1, def)
22+
return randIntRange(rnd, 1, def)
2323
}
2424

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

29-
func nonEmptyRandInt64Range(min int64, max int64, def int64) int64 {
29+
func nonEmptyRandInt64Range(rnd *rand.Rand, min int64, max int64, def int64) int64 {
3030
if max > min && min > 0 {
31-
return randInt64Range(min, max)
31+
return randInt64Range(rnd, min, max)
3232
}
33-
return randInt64Range(1, def)
33+
return randInt64Range(rnd, 1, def)
3434
}
3535

36-
func randFloat32Range(min float32, max float32) float32 {
37-
return rand.Float32() * (max - min)
36+
func randFloat32Range(rnd *rand.Rand, min float32, max float32) float32 {
37+
return rnd.Float32() * (max - min)
3838
}
3939

40-
func nonEmptyRandFloat32Range(min float32, max float32, def float32) float32 {
40+
func nonEmptyRandFloat32Range(rnd *rand.Rand, min float32, max float32, def float32) float32 {
4141
if max > min && min > 0 {
42-
return randFloat32Range(min, max)
42+
return randFloat32Range(rnd, min, max)
4343
}
44-
return randFloat32Range(1, def)
44+
return randFloat32Range(rnd, 1, def)
4545
}
4646

47-
func randFloat64Range(min float64, max float64) float64 {
48-
return rand.Float64() * (max - min)
47+
func randFloat64Range(rnd *rand.Rand, min float64, max float64) float64 {
48+
return rnd.Float64() * (max - min)
4949
}
5050

51-
func nonEmptyRandFloat64Range(min float64, max float64, def float64) float64 {
51+
func nonEmptyRandFloat64Range(rnd *rand.Rand, min float64, max float64, def float64) float64 {
5252
if max > min && min > 0 {
53-
return randFloat64Range(min, max)
53+
return randFloat64Range(rnd, min, max)
5454
}
55-
return randFloat64Range(1, def)
55+
return randFloat64Range(rnd, 1, def)
5656
}
5757

58-
func randString(len int) string {
59-
return nonEmptyRandStringWithTime(len, time.Now().UTC())
60-
}
61-
62-
func randStringWithTime(len int, t time.Time) string {
58+
func randStringWithTime(rnd *rand.Rand, len int, t time.Time) string {
6359
id, _ := ksuid.NewRandomWithTime(t)
6460

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

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

7672
return buf.String()[:len]
7773
}
7874

79-
func nonEmptyRandStringWithTime(len int, t time.Time) string {
75+
func nonEmptyRandStringWithTime(rnd *rand.Rand, len int, t time.Time) string {
8076
if len <= 0 {
8177
len = 1
8278
}
83-
return randStringWithTime(len, t)
79+
return randStringWithTime(rnd, len, t)
8480
}
8581

86-
func randDate() string {
87-
time := randTime()
82+
func randDate(rnd *rand.Rand) string {
83+
time := randTime(rnd)
8884
return time.Format("2006-01-02")
8985
}
9086

91-
func randTime() time.Time {
87+
func randTime(rnd *rand.Rand) time.Time {
9288
min := time.Date(1970, 1, 0, 0, 0, 0, 0, time.UTC).Unix()
9389
max := time.Date(2024, 1, 0, 0, 0, 0, 0, time.UTC).Unix()
9490

95-
sec := rand.Int63n(max-min) + min
91+
sec := rnd.Int63n(max-min) + min
9692
return time.Unix(sec, 0)
9793
}
9894

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

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

107-
func randIpV4Address(v, pos int) string {
103+
func randIpV4Address(rnd *rand.Rand, v, pos int) string {
108104
if pos < 0 || pos > 4 {
109105
panic(fmt.Sprintf("invalid position for the desired value of the IP part %d, 0-3 supported", pos))
110106
}
@@ -116,7 +112,7 @@ func randIpV4Address(v, pos int) string {
116112
if i == pos {
117113
blocks = append(blocks, strconv.Itoa(v))
118114
} else {
119-
blocks = append(blocks, strconv.Itoa(rand.Intn(255)))
115+
blocks = append(blocks, strconv.Itoa(rnd.Intn(255)))
120116
}
121117
}
122118
return strings.Join(blocks, ".")

datautils_test.go

+12-9
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
package gemini
22

33
import (
4+
"math/rand"
45
"testing"
56
"testing/quick"
67
"time"
78
)
89

10+
var rnd = rand.New(rand.NewSource(time.Now().UnixNano()))
11+
912
func TestNonEmptyRandRange(t *testing.T) {
1013
f := func(x, y int) bool {
11-
r := nonEmptyRandIntRange(x, y, 10)
14+
r := nonEmptyRandIntRange(rnd, x, y, 10)
1215
return r > 0
1316
}
1417
if err := quick.Check(f, nil); err != nil {
@@ -18,7 +21,7 @@ func TestNonEmptyRandRange(t *testing.T) {
1821

1922
func TestNonEmptyRandRange64(t *testing.T) {
2023
f := func(x, y int) bool {
21-
r := nonEmptyRandIntRange(x, y, 10)
24+
r := nonEmptyRandIntRange(rnd, x, y, 10)
2225
return r > 0
2326
}
2427
if err := quick.Check(f, nil); err != nil {
@@ -28,7 +31,7 @@ func TestNonEmptyRandRange64(t *testing.T) {
2831

2932
func TestNonEmptyRandFloat32Range(t *testing.T) {
3033
f := func(x, y float32) bool {
31-
r := nonEmptyRandFloat32Range(x, y, 10)
34+
r := nonEmptyRandFloat32Range(rnd, x, y, 10)
3235
return r > 0
3336
}
3437
if err := quick.Check(f, nil); err != nil {
@@ -38,7 +41,7 @@ func TestNonEmptyRandFloat32Range(t *testing.T) {
3841

3942
func TestNonEmptyRandFloat64Range(t *testing.T) {
4043
f := func(x, y float64) bool {
41-
r := nonEmptyRandFloat64Range(x, y, 10)
44+
r := nonEmptyRandFloat64Range(rnd, x, y, 10)
4245
return r > 0
4346
}
4447
if err := quick.Check(f, nil); err != nil {
@@ -50,7 +53,7 @@ func TestNonEmptyRandString(t *testing.T) {
5053
// TODO: Figure out why this is so horribly slow...
5154
tt := time.Now()
5255
f := func(len int32) bool {
53-
r := nonEmptyRandStringWithTime(int(len), tt)
56+
r := nonEmptyRandStringWithTime(rnd, int(len), tt)
5457
return r != ""
5558
}
5659
cfg := &quick.Config{MaxCount: 10}
@@ -64,15 +67,15 @@ var bench_r string
6467
func BenchmarkNonEmptyRandStringWithTime(b *testing.B) {
6568
tt := time.Now()
6669
for i := 0; i < b.N; i++ {
67-
bench_r = nonEmptyRandStringWithTime(30, tt)
70+
bench_r = nonEmptyRandStringWithTime(rnd, 30, tt)
6871
}
6972
}
7073

7174
func BenchmarkNonEmptyRandStringWithTimeParallel(b *testing.B) {
7275
tt := time.Now()
7376
b.RunParallel(func(pb *testing.PB) {
7477
for pb.Next() {
75-
bench_r = nonEmptyRandStringWithTime(30, tt)
78+
bench_r = nonEmptyRandStringWithTime(rnd, 30, tt)
7679
}
7780
})
7881
}
@@ -81,14 +84,14 @@ var bench_rr int
8184

8285
func BenchmarkNonEmptyRandRange(b *testing.B) {
8386
for i := 0; i < b.N; i++ {
84-
bench_rr = nonEmptyRandIntRange(0, 50, 30)
87+
bench_rr = nonEmptyRandIntRange(rnd, 0, 50, 30)
8588
}
8689
}
8790

8891
var bench_rr64 int64
8992

9093
func BenchmarkNonEmptyRandRange64(b *testing.B) {
9194
for i := 0; i < b.N; i++ {
92-
bench_rr64 = nonEmptyRandInt64Range(0, 50, 30)
95+
bench_rr64 = nonEmptyRandInt64Range(rnd, 0, 50, 30)
9396
}
9497
}

0 commit comments

Comments
 (0)