Skip to content

Commit a571c52

Browse files
seankhliaopellareddmathieu
authored
all: replace math/rand with math/rand/v2 (#6732)
Update to new stdlib apis. The new Float64 is uniform, which resolves a long comment. https://cs.opensource.google/go/go/+/refs/tags/go1.24.2:src/math/rand/v2/rand.go;l=209 > // There are exactly 1<<53 float64s in [0,1). Use Intn(1<<53) / (1<<53). return float64(r.Uint64()<<11>>11) / (1 << 53) ``` goos: linux goarch: amd64 pkg: go.opentelemetry.io/otel/sdk/trace cpu: 12th Gen Intel(R) Core(TM) i7-1260P │ old.txt │ new.txt │ │ sec/op │ sec/op vs base │ TraceStart/with_a_simple_span-16 387.1n ± 5% 306.8n ± 15% -20.73% (p=0.000 n=10) TraceStart/with_several_links-16 542.2n ± 5% 501.0n ± 2% -7.61% (p=0.000 n=10) TraceStart/with_attributes-16 521.4n ± 14% 571.6n ± 6% +9.64% (p=0.009 n=10) geomean 478.3n 444.6n -7.05% │ old.txt │ new.txt │ │ B/op │ B/op vs base │ TraceStart/with_a_simple_span-16 528.0 ± 0% 528.0 ± 0% ~ (p=1.000 n=10) ¹ TraceStart/with_several_links-16 704.0 ± 0% 704.0 ± 0% ~ (p=1.000 n=10) ¹ TraceStart/with_attributes-16 784.0 ± 0% 784.0 ± 0% ~ (p=1.000 n=10) ¹ geomean 663.0 663.0 +0.00% ¹ all samples are equal │ old.txt │ new.txt │ │ allocs/op │ allocs/op vs base │ TraceStart/with_a_simple_span-16 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=10) ¹ TraceStart/with_several_links-16 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=10) ¹ TraceStart/with_attributes-16 4.000 ± 0% 4.000 ± 0% ~ (p=1.000 n=10) ¹ geomean 2.884 2.884 +0.00% ¹ all samples are equal ``` --------- Co-authored-by: Robert Pająk <[email protected]> Co-authored-by: Damien Mathieu <[email protected]>
1 parent f410084 commit a571c52

File tree

10 files changed

+41
-71
lines changed

10 files changed

+41
-71
lines changed

baggage/baggage_test.go

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ package baggage
55

66
import (
77
"fmt"
8-
"math/rand"
8+
"math/rand/v2"
99
"slices"
1010
"strings"
1111
"testing"
@@ -17,12 +17,8 @@ import (
1717
"go.opentelemetry.io/otel/internal/baggage"
1818
)
1919

20-
var rng *rand.Rand
21-
22-
func init() {
23-
// Seed with a static value to ensure deterministic results.
24-
rng = rand.New(rand.NewSource(1))
25-
}
20+
// Seed with a static value to ensure deterministic results.
21+
var rng = rand.New(rand.NewChaCha8([32]byte{}))
2622

2723
func TestValidateKeyChar(t *testing.T) {
2824
// ASCII only
@@ -255,7 +251,7 @@ func key(n int) string {
255251

256252
b := make([]rune, n)
257253
for i := range b {
258-
b[i] = r[rng.Intn(len(r))]
254+
b[i] = r[rng.IntN(len(r))]
259255
}
260256
return string(b)
261257
}

bridge/opencensus/internal/ocmetric/metric_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"errors"
88
"fmt"
99
"math"
10-
"math/rand"
10+
"math/rand/v2"
1111
"reflect"
1212
"strconv"
1313
"testing"
@@ -1189,7 +1189,7 @@ func BenchmarkConvertExemplar(b *testing.B) {
11891189
for i := range data {
11901190
a := make(ocmetricdata.Attachments, attchmentsN)
11911191
for j := 0; j < attchmentsN; j++ {
1192-
a[strconv.Itoa(j)] = rand.Int63()
1192+
a[strconv.Itoa(j)] = rand.Int64()
11931193
}
11941194
data[i] = &ocmetricdata.Exemplar{
11951195
Value: rand.NormFloat64(),

bridge/opentracing/internal/mock.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ package internal // import "go.opentelemetry.io/otel/bridge/opentracing/internal
55

66
import (
77
"context"
8-
"math/rand"
8+
"math/rand/v2"
99
"reflect"
1010
"sync"
1111
"time"
@@ -44,7 +44,7 @@ type MockTracer struct {
4444
TraceFlags trace.TraceFlags
4545

4646
randLock sync.Mutex
47-
rand *rand.Rand
47+
rand *rand.ChaCha8
4848
}
4949

5050
var (
@@ -53,13 +53,15 @@ var (
5353
)
5454

5555
func NewMockTracer() *MockTracer {
56+
u := rand.Uint32()
57+
seed := [32]byte{byte(u), byte(u >> 8), byte(u >> 16), byte(u >> 24)}
5658
return &MockTracer{
5759
FinishedSpans: nil,
5860
SpareTraceIDs: nil,
5961
SpareSpanIDs: nil,
6062
SpareContextKeyValues: nil,
6163

62-
rand: rand.New(rand.NewSource(time.Now().Unix())),
64+
rand: rand.NewChaCha8(seed),
6365
}
6466
}
6567

metric/example_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"context"
88
"database/sql"
99
"fmt"
10-
"math/rand"
10+
"math/rand/v2"
1111
"net/http"
1212
"runtime"
1313
"time"
@@ -159,7 +159,7 @@ func ExampleMeter_gauge() {
159159
getCPUFanSpeed := func() int64 {
160160
// Generates a random fan speed for demonstration purpose.
161161
// In real world applications, replace this to get the actual fan speed.
162-
return int64(1500 + rand.Intn(1000))
162+
return int64(1500 + rand.IntN(1000))
163163
}
164164

165165
fanSpeedSubscription := make(chan int64, 1)
@@ -170,7 +170,7 @@ func ExampleMeter_gauge() {
170170
// Synchronous gauges are used when the measurement cycle is
171171
// synchronous to an external change.
172172
// Simulate that external cycle here.
173-
time.Sleep(time.Duration(rand.Intn(3)) * time.Second)
173+
time.Sleep(time.Duration(rand.IntN(3)) * time.Second)
174174
fanSpeed := getCPUFanSpeed()
175175
fanSpeedSubscription <- fanSpeed
176176
}

sdk/metric/exemplar/fixed_size_reservoir.go

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ package exemplar // import "go.opentelemetry.io/otel/sdk/metric/exemplar"
66
import (
77
"context"
88
"math"
9-
"math/rand"
9+
"math/rand/v2"
1010
"time"
1111

1212
"go.opentelemetry.io/otel/attribute"
@@ -44,18 +44,11 @@ type FixedSizeReservoir struct {
4444
// w is the largest random number in a distribution that is used to compute
4545
// the next next.
4646
w float64
47-
48-
// rng is used to make sampling decisions.
49-
//
50-
// Do not use crypto/rand. There is no reason for the decrease in performance
51-
// given this is not a security sensitive decision.
52-
rng *rand.Rand
5347
}
5448

5549
func newFixedSizeReservoir(s *storage) *FixedSizeReservoir {
5650
r := &FixedSizeReservoir{
5751
storage: s,
58-
rng: rand.New(rand.NewSource(time.Now().UnixNano())),
5952
}
6053
r.reset()
6154
return r
@@ -64,26 +57,15 @@ func newFixedSizeReservoir(s *storage) *FixedSizeReservoir {
6457
// randomFloat64 returns, as a float64, a uniform pseudo-random number in the
6558
// open interval (0.0,1.0).
6659
func (r *FixedSizeReservoir) randomFloat64() float64 {
67-
// TODO: This does not return a uniform number. rng.Float64 returns a
68-
// uniformly random int in [0,2^53) that is divided by 2^53. Meaning it
69-
// returns multiples of 2^-53, and not all floating point numbers between 0
70-
// and 1 (i.e. for values less than 2^-4 the 4 last bits of the significand
71-
// are always going to be 0).
72-
//
73-
// An alternative algorithm should be considered that will actually return
74-
// a uniform number in the interval (0,1). For example, since the default
75-
// rand source provides a uniform distribution for Int63, this can be
76-
// converted following the prototypical code of Mersenne Twister 64 (Takuji
77-
// Nishimura and Makoto Matsumoto:
78-
// http://www.math.sci.hiroshima-u.ac.jp/m-mat/MT/VERSIONS/C-LANG/mt19937-64.c)
60+
// TODO: Use an algorithm that avoids rejection sampling. For example:
7961
//
80-
// (float64(rng.Int63()>>11) + 0.5) * (1.0 / 4503599627370496.0)
81-
//
82-
// There are likely many other methods to explore here as well.
83-
84-
f := r.rng.Float64()
62+
// const precision = 1 << 53 // 2^53
63+
// // Generate an integer in [1, 2^53 - 1]
64+
// v := rand.Uint64() % (precision - 1) + 1
65+
// return float64(v) / float64(precision)
66+
f := rand.Float64()
8567
for f == 0 {
86-
f = r.rng.Float64()
68+
f = rand.Float64()
8769
}
8870
return f
8971
}
@@ -146,7 +128,7 @@ func (r *FixedSizeReservoir) Offer(ctx context.Context, t time.Time, n Value, a
146128
} else {
147129
if r.count == r.next {
148130
// Overwrite a random existing measurement with the one offered.
149-
idx := int(r.rng.Int63n(int64(cap(r.store))))
131+
idx := int(rand.Int64N(int64(cap(r.store))))
150132
r.store[idx] = newMeasurement(ctx, t, n, a)
151133
r.advance()
152134
}

sdk/metric/exemplar/fixed_size_reservoir_test.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@ package exemplar
66
import (
77
"context"
88
"math"
9-
"math/rand"
9+
"math/rand/v2"
1010
"slices"
1111
"testing"
12-
"time"
1312

1413
"github.com/stretchr/testify/assert"
1514
)
@@ -28,7 +27,10 @@ func TestNewFixedSizeReservoirSamplingCorrectness(t *testing.T) {
2827
intensity := 0.1
2928
sampleSize := 1000
3029

31-
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
30+
u := rand.Uint32()
31+
seed := [32]byte{byte(u), byte(u >> 8), byte(u >> 16), byte(u >> 24)}
32+
t.Logf("rng seed: %x", seed)
33+
rng := rand.New(rand.NewChaCha8(seed))
3234

3335
data := make([]float64, sampleSize*1000)
3436
for i := range data {

sdk/resource/benchmark_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ package resource_test
55

66
import (
77
"fmt"
8-
"math/rand"
8+
"math/rand/v2"
99
"testing"
1010

1111
"go.opentelemetry.io/otel/attribute"
@@ -21,18 +21,18 @@ func makeAttrs(n int) (_, _ *resource.Resource) {
2121
for i := 0; i < n; i++ {
2222
var k string
2323
for {
24-
k = fmt.Sprint("k", rand.Intn(1000000000))
24+
k = fmt.Sprint("k", rand.IntN(1000000000))
2525
if !used[k] {
2626
used[k] = true
2727
break
2828
}
2929
}
30-
l1[i] = attribute.String(k, fmt.Sprint("v", rand.Intn(1000000000)))
30+
l1[i] = attribute.String(k, fmt.Sprint("v", rand.IntN(1000000000)))
3131

3232
if rand.Float64() < conflict {
3333
l2[i] = l1[i]
3434
} else {
35-
l2[i] = attribute.String(k, fmt.Sprint("v", rand.Intn(1000000000)))
35+
l2[i] = attribute.String(k, fmt.Sprint("v", rand.IntN(1000000000)))
3636
}
3737
}
3838
return resource.NewSchemaless(l1...), resource.NewSchemaless(l2...)

sdk/trace/id_generator.go

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@ package trace // import "go.opentelemetry.io/otel/sdk/trace"
55

66
import (
77
"context"
8-
crand "crypto/rand"
98
"encoding/binary"
10-
"math/rand"
11-
"sync"
9+
"math/rand/v2"
1210

1311
"go.opentelemetry.io/otel/trace"
1412
)
@@ -29,20 +27,15 @@ type IDGenerator interface {
2927
// must never be done outside of a new major release.
3028
}
3129

32-
type randomIDGenerator struct {
33-
sync.Mutex
34-
randSource *rand.Rand
35-
}
30+
type randomIDGenerator struct{}
3631

3732
var _ IDGenerator = &randomIDGenerator{}
3833

3934
// NewSpanID returns a non-zero span ID from a randomly-chosen sequence.
4035
func (gen *randomIDGenerator) NewSpanID(ctx context.Context, traceID trace.TraceID) trace.SpanID {
41-
gen.Lock()
42-
defer gen.Unlock()
4336
sid := trace.SpanID{}
4437
for {
45-
_, _ = gen.randSource.Read(sid[:])
38+
binary.NativeEndian.PutUint64(sid[:], rand.Uint64())
4639
if sid.IsValid() {
4740
break
4841
}
@@ -53,18 +46,17 @@ func (gen *randomIDGenerator) NewSpanID(ctx context.Context, traceID trace.Trace
5346
// NewIDs returns a non-zero trace ID and a non-zero span ID from a
5447
// randomly-chosen sequence.
5548
func (gen *randomIDGenerator) NewIDs(ctx context.Context) (trace.TraceID, trace.SpanID) {
56-
gen.Lock()
57-
defer gen.Unlock()
5849
tid := trace.TraceID{}
5950
sid := trace.SpanID{}
6051
for {
61-
_, _ = gen.randSource.Read(tid[:])
52+
binary.NativeEndian.PutUint64(tid[:8], rand.Uint64())
53+
binary.NativeEndian.PutUint64(tid[8:], rand.Uint64())
6254
if tid.IsValid() {
6355
break
6456
}
6557
}
6658
for {
67-
_, _ = gen.randSource.Read(sid[:])
59+
binary.NativeEndian.PutUint64(sid[:], rand.Uint64())
6860
if sid.IsValid() {
6961
break
7062
}
@@ -73,9 +65,5 @@ func (gen *randomIDGenerator) NewIDs(ctx context.Context) (trace.TraceID, trace.
7365
}
7466

7567
func defaultIDGenerator() IDGenerator {
76-
gen := &randomIDGenerator{}
77-
var rngSeed int64
78-
_ = binary.Read(crand.Reader, binary.LittleEndian, &rngSeed)
79-
gen.randSource = rand.New(rand.NewSource(rngSeed))
80-
return gen
68+
return &randomIDGenerator{}
8169
}

sdk/trace/provider_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"context"
88
"errors"
99
"fmt"
10-
"math/rand"
10+
"math/rand/v2"
1111
"testing"
1212

1313
"github.com/stretchr/testify/assert"

sdk/trace/sampling_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ package trace
66
import (
77
"context"
88
"fmt"
9-
"math/rand"
9+
"math/rand/v2"
1010
"testing"
1111

1212
"github.com/stretchr/testify/assert"

0 commit comments

Comments
 (0)