-
Notifications
You must be signed in to change notification settings - Fork 327
Allow creating additional View universes. #1196
Changes from 4 commits
65c9cae
7ee9440
c96d579
c4c5f2d
e7bc6a0
79ead5a
4bd9e42
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,10 +31,17 @@ func init() { | |
} | ||
} | ||
|
||
// Recorder is a subset of the view.Meter interface which only includes | ||
// the Record method, to avoid circular imports between stats and view. | ||
type Recorder interface { | ||
Record(*tag.Map, interface{}, map[string]interface{}) | ||
} | ||
|
||
type recordOptions struct { | ||
attachments metricdata.Attachments | ||
mutators []tag.Mutator | ||
measurements []Measurement | ||
recorder Recorder | ||
} | ||
|
||
// WithAttachments applies provided exemplar attachments. | ||
|
@@ -58,6 +65,14 @@ func WithMeasurements(measurements ...Measurement) Options { | |
} | ||
} | ||
|
||
// WithMeter records the measurements to the specified `view.Meter`, rather | ||
// than to the global metrics recorder. | ||
func WithMeter(meter Recorder) Options { | ||
evankanderson marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Performance of RecordWithOptions WithMeter is 2/3rd of RecordWithOptions without meter
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using the following, I get 231 ns/op, rather than 270 ns/op: func BenchmarkRecord8_WithRecorder(b *testing.B) {
ctx := context.Background()
meter := view.NewMeter()
meter.Start()
defer meter.Stop()
b.ResetTimer()
for i := 0; i < b.N; i++ {
stats.RecordWithOptions(ctx, stats.WithRecorder(meter), stats.WithMeasurements(m.M(1), m.M(1), m.M(1), m.M(1), m.M(1), m.M(1), m.M(1), m.M(1)))
}
b.StopTimer()
} For the _Parallel case, I get 117 ns/op vs 108 ns/op:
I can avoid the extra allocation and make the time between BenchmarkRecord8_WithRecorder and BenchmarkRecord8 match by pre-creating the rec := stats.WithRecorder(meter)
for (....) {
stats.RecordWithOptions(ctx, rec, stats.WithMeasurements(....)
} I note that the current benchmark doesn't perform any aggregation, so it's actually testing the shortcut path AFAICT. Adding a view which is tracking a simple Sum (no tags) increases the benchmark time like so:
Note that using a view here also affects any subsequent benchmarks for the same measurement, because the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. BenchmarkRecord8_WithRecorder and BenchmarkRecord8 match by pre-creating the
+1. |
||
return func(ro *recordOptions) { | ||
ro.recorder = meter | ||
} | ||
} | ||
|
||
// Options apply changes to recordOptions. | ||
type Options func(*recordOptions) | ||
|
||
|
@@ -93,6 +108,9 @@ func RecordWithOptions(ctx context.Context, ros ...Options) error { | |
return nil | ||
} | ||
recorder := internal.DefaultRecorder | ||
if o.recorder != nil { | ||
recorder = o.recorder.Record | ||
} | ||
if recorder == nil { | ||
return nil | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
keep this definition and simply include this in the Meter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. I updated the comment, too.
What do you think of changing the second argument to Record to be
[]stats.Measurement
rather than the currentinterface{}
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that is better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It turns out that this introduces an import cycle:
Would you prefer that I move internal.DefaultRecorder out of
internal
, or do something like create an InternalMeasurement struct or interface and embed it in stats.Measurement?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would say leave it as interface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It turns out that
stats.Measurement.Measure()
returns a stats.Measure, which seemed like a bridge too far in terms of putting things into Internal.Take a look at 6003f86 for what that change would look like. I'm slightly concerned about moving DefaultRecorder from the internal package (and making it more public/obvious).
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reverted this to stick to interfaces.