Skip to content
This repository was archived by the owner on Jul 31, 2023. It is now read-only.

Commit 983446b

Browse files
authored
Don't record measurements with no subscription (#600)
Suggested by Ramon, immediately dropping the measurements from the measures with no subscription rather than doing it at a later time significantly improves the performance on the critical path. Before: BenchmarkRecord0-8 500000000 3.09 ns/op BenchmarkRecord1-8 5000000 366 ns/op BenchmarkRecord8-8 3000000 412 ns/op BenchmarkRecord8_Parallel-8 2000000 804 ns/op BenchmarkRecord8_8Tags-8 3000000 415 ns/op After: BenchmarkRecord0-8 1000000000 2.58 ns/op BenchmarkRecord1-8 30000000 36.9 ns/op BenchmarkRecord8-8 20000000 89.4 ns/op BenchmarkRecord8_Parallel-8 30000000 44.8 ns/op BenchmarkRecord8_8Tags-8 20000000 90.1 ns/op
1 parent 92b618f commit 983446b

File tree

6 files changed

+72
-10
lines changed

6 files changed

+72
-10
lines changed

stats/internal/record.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ import (
1818
"go.opencensus.io/tag"
1919
)
2020

21-
type Recorder func(*tag.Map, interface{})
22-
2321
// DefaultRecorder will be called for each Record call.
24-
var DefaultRecorder Recorder = nil
22+
var DefaultRecorder func(*tag.Map, interface{})
23+
24+
// SubscriptionReporter reports when a view subscribed with a measure.
25+
var SubscriptionReporter func(measure string)

stats/measure.go

+19-3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"errors"
2020
"fmt"
2121
"sync"
22+
"sync/atomic"
2223

2324
"go.opencensus.io/stats/internal"
2425
)
@@ -37,14 +38,27 @@ type Measure interface {
3738
Name() string
3839
Description() string
3940
Unit() string
41+
42+
subscribe()
43+
subscribed() bool
4044
}
4145

4246
type measure struct {
47+
subs int32 // access atomically
48+
4349
name string
4450
description string
4551
unit string
4652
}
4753

54+
func (m *measure) subscribe() {
55+
atomic.StoreInt32(&m.subs, 1)
56+
}
57+
58+
func (m *measure) subscribed() bool {
59+
return atomic.LoadInt32(&m.subs) == 1
60+
}
61+
4862
// Name returns the name of the measure.
4963
func (m *measure) Name() string {
5064
return m.name
@@ -61,10 +75,12 @@ func (m *measure) Unit() string {
6175
}
6276

6377
var (
64-
mu sync.RWMutex
65-
measures = make(map[string]Measure)
66-
errDuplicate = errors.New("duplicate measure name")
78+
mu sync.RWMutex
79+
measures = make(map[string]Measure)
80+
)
6781

82+
var (
83+
errDuplicate = errors.New("duplicate measure name")
6884
errMeasureNameTooLong = fmt.Errorf("measure name cannot be longer than %v", internal.MaxNameLength)
6985
)
7086

stats/measure_float64.go

+13-2
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,21 @@ type Float64Measure struct {
2020
measure
2121
}
2222

23+
func (f *Float64Measure) subscribe() {
24+
f.measure.subscribe()
25+
}
26+
27+
func (f *Float64Measure) subscribed() bool {
28+
return f.measure.subscribed()
29+
}
30+
2331
// M creates a new float64 measurement.
2432
// Use Record to record measurements.
25-
func (m *Float64Measure) M(v float64) Measurement {
26-
return Measurement{m: m, v: v}
33+
func (f *Float64Measure) M(v float64) Measurement {
34+
if !f.subscribed() {
35+
return Measurement{}
36+
}
37+
return Measurement{m: f, v: v}
2738
}
2839

2940
// Float64 creates a new measure of type Float64Measure. It returns

stats/measure_int64.go

+13-2
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,21 @@ type Int64Measure struct {
2020
measure
2121
}
2222

23+
func (i *Int64Measure) subscribe() {
24+
i.measure.subscribe()
25+
}
26+
27+
func (i *Int64Measure) subscribed() bool {
28+
return i.measure.subscribed()
29+
}
30+
2331
// M creates a new int64 measurement.
2432
// Use Record to record measurements.
25-
func (m *Int64Measure) M(v int64) Measurement {
26-
return Measurement{m: m, v: float64(v)}
33+
func (i *Int64Measure) M(v int64) Measurement {
34+
if !i.subscribed() {
35+
return Measurement{}
36+
}
37+
return Measurement{m: i, v: float64(v)}
2738
}
2839

2940
// Int64 creates a new measure of type Int64Measure. It returns an

stats/record.go

+18
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,30 @@ import (
2222
"go.opencensus.io/tag"
2323
)
2424

25+
func init() {
26+
internal.SubscriptionReporter = func(measure string) {
27+
mu.Lock()
28+
measures[measure].subscribe()
29+
mu.Unlock()
30+
}
31+
}
32+
2533
// Record records one or multiple measurements with the same tags at once.
2634
// If there are any tags in the context, measurements will be tagged with them.
2735
func Record(ctx context.Context, ms ...Measurement) {
2836
if len(ms) == 0 {
2937
return
3038
}
39+
var record bool
40+
for _, m := range ms {
41+
if (m != Measurement{}) {
42+
record = true
43+
break
44+
}
45+
}
46+
if !record {
47+
return
48+
}
3149
if internal.DefaultRecorder != nil {
3250
internal.DefaultRecorder(tag.FromContext(ctx), ms)
3351
}

stats/view/worker_commands.go

+5
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"time"
2323

2424
"go.opencensus.io/stats"
25+
"go.opencensus.io/stats/internal"
2526
"go.opencensus.io/tag"
2627
)
2728

@@ -57,6 +58,7 @@ func (cmd *subscribeToViewReq) handleCommand(w *worker) {
5758
errstr = append(errstr, fmt.Sprintf("%s: %v", view.Name, err))
5859
continue
5960
}
61+
internal.SubscriptionReporter(view.Measure.Name())
6062
vi.subscribe()
6163
}
6264
if len(errstr) > 0 {
@@ -135,6 +137,9 @@ type recordReq struct {
135137

136138
func (cmd *recordReq) handleCommand(w *worker) {
137139
for _, m := range cmd.ms {
140+
if (m == stats.Measurement{}) { // not subscribed
141+
continue
142+
}
138143
ref := w.getMeasureRef(m.Measure().Name())
139144
for v := range ref.views {
140145
v.addSample(cmd.tm, m.Value())

0 commit comments

Comments
 (0)