Skip to content

Commit 8f869ae

Browse files
deanward81Dean WardNickCraverNick Craver
authored
New API (#41)
* This commit changes the overall API surface for metrics to make them simpler to use in applications. When digging into our usages of BosunReporter and StackExchange.Metrics we discovered that the only time we derived from one of the built-in metric types was to add tags to our metrics. This felt like a lot of boilerplate code: ```c# [GaugeAggregator(AggregateMode.Average)] [GaugeAggregator(AggregateMode.Max)] [GaugeAggregator(AggregateMode.Percentile, 0.95)] [GaugeAggregator(AggregateMode.Percentile, 0.99)] [GaugeAggregator(AggregateMode.Median)] [GaugeAggregator(AggregateMode.Min)] private class RequestTimingGauge : AggregateGauge { public RequestTimingGauge(HttpStatusCode status_code) { this.status_code = status_code; } [MetricTag] public readonly HttpStatusCode status_code; } public class Metrics { private readonly Counter _httpRequests; private readonly MetricGroup<HttpStatusCode, RequestTimingGauge> _httpTimings; public Metrics() { // creation _httpRequests = collector.GetMetric<Counter>("http.tr", "milliseconds", "Duration of an HTTP request"); _httpTimings = collector.GetMetricGroup("http.tr", "milliseconds", "Duration of an HTTP request", statusCode => new RequestTimingGauge(statusCode)); } public void OnHttpRequest(HttpStatusCode statusCode, double durationMs) { // usage _httpRequests.Increment(); _httpTimings.Add(statusCode).Record(durationMs); } } ``` Note how the derived class specifies attributes to indicate aggregation characteristics of an `AggregateGauge` and the properties / field metadata dictates the tags that are applied to the metric. In order to construct the metric we need to pass a factory that understands how to do so and to use it we need to call `Add` with the tag values followed by `Record` for a `Gauge` or `Increment` for a `Counter`. We've decided to simplify this a little and consolidate the work done with `MetricSet` to provide a consistent interface to metric creation and consumption. To do that we've worked to de-couple the idea of a metric from the thing collecting the metrics (although there should still only be one instance of `MetricsCollector` otherwise readings can become inconsistent due to snapshotting constraints). We've also done away with the need to derive from one of the built-in metrics to annotate them with tags. This means no more factories and no more extra classes kicking around. In most metric platforms tags inevitably end up as an untyped map of string => string, so we've tried to keep it as simple as that, but layering strong-typing where appropriate. An application should create one or more `MetricSource` instances and use those to add metrics that are consumed elsewhere. This is more dependency-injection friendly which is important in .NET Core. `MetricSource` supercedes `MetricSet` and all pre-packaged metrics (e.g. dotnet runtime metrics, etc.) now use this abstraction. In most of our cases we've found that a `MetricSource` per functional area or for an entire application seems to sense. ```c# public class AppMetricSource : MetricSource { private readonly Counter _httpRequests; private readonly AggregateGauge<HttpStatusCode> _httpTimings; public AppMetricSource() { // creation _httpRequests = AddCounter("http.requests", "requests", "Number of HTTP requests"); _httpTimings = AddAggregateGauge(GaugeAggregators.Default, "http.tr", "milliseconds", "Duration of an HTTP request", new MetricTag<HttpStatusCode>("status_code")); } public void OnHttpRequest(HttpStatusCode statusCode, double durationMs) { // usage _httpRequests.Increment(); _httpTimings.Record(statusCode, durationMs); } } ``` You can see that we have consistent types - e.g. a `Counter` with metrics becomes `Counter<TTag1, ..., TTagN>` and usage is less complex. There's no need for custom metric classes or attributes and the collector does not need to be instantiated to create the metrics. A `MetricsCollector` is now passed a set of `MetricSource` instances that are used to retrieve readings from the metrics contained within. You can call `GetReadings(DateTime)` on a metric, a tagged metric and one or more `MetricSource` to retrieve readings without a collector. However, importantly, as before there should only be one collector per application. If attach a collector or you call `GetReadings` then readings are collected which results in a snapshot and resets the state of a metric. | Method | iterations | Mean | Error | StdDev | Median | Gen 0 | Gen 1 | Gen 2 | Allocated | |------------------------------------ |----------- |-----------:|----------:|----------:|-----------:|------:|------:|------:|----------:| | CounterIncrement | ? | 4.136 ns | 0.0173 ns | 0.0162 ns | 4.133 ns | - | - | - | - | | SamplingSet | ? | 4.095 ns | 0.0093 ns | 0.0082 ns | 4.094 ns | - | - | - | - | | Tagged_CounterIncrement_String_One | 1 | 24.308 ns | 0.5044 ns | 0.5606 ns | 23.905 ns | - | - | - | - | | Tagged_CounterIncrement_String_Many | 1 | 68.818 ns | 0.2411 ns | 0.2013 ns | 68.797 ns | - | - | - | - | | Tagged_CounterIncrement_Int_One | 1 | 16.425 ns | 0.0486 ns | 0.0431 ns | 16.412 ns | - | - | - | - | | Tagged_CounterIncrement_Int_Many | 1 | 48.844 ns | 0.2609 ns | 0.2179 ns | 48.830 ns | - | - | - | - | | Tagged_CounterIncrement_Enum | 1 | 17.142 ns | 0.3665 ns | 0.4074 ns | 17.370 ns | - | - | - | - | | Tagged_CounterIncrement_Enum_Many | 1 | 48.831 ns | 0.1340 ns | 0.1046 ns | 48.834 ns | - | - | - | - | | Tagged_CounterIncrement_String_One | 2 | 48.075 ns | 0.3933 ns | 0.3487 ns | 47.975 ns | - | - | - | - | | Tagged_CounterIncrement_String_Many | 2 | 143.109 ns | 0.1595 ns | 0.1332 ns | 143.108 ns | - | - | - | - | | Tagged_CounterIncrement_Int_One | 2 | 36.852 ns | 0.0525 ns | 0.0438 ns | 36.850 ns | - | - | - | - | | Tagged_CounterIncrement_Int_Many | 2 | 97.427 ns | 0.1823 ns | 0.1616 ns | 97.351 ns | - | - | - | - | | Tagged_CounterIncrement_Enum | 2 | 32.932 ns | 0.0881 ns | 0.0781 ns | 32.908 ns | - | - | - | - | | Tagged_CounterIncrement_Enum_Many | 2 | 105.989 ns | 0.1511 ns | 0.1414 ns | 106.013 ns | - | - | - | - | | Tagged_CounterIncrement_String_One | 5 | 121.504 ns | 0.1993 ns | 0.1767 ns | 121.505 ns | - | - | - | - | | Tagged_CounterIncrement_String_Many | 5 | 343.593 ns | 0.5036 ns | 0.4464 ns | 343.566 ns | - | - | - | - | | Tagged_CounterIncrement_Int_One | 5 | 85.827 ns | 0.0892 ns | 0.0790 ns | 85.847 ns | - | - | - | - | | Tagged_CounterIncrement_Int_Many | 5 | 246.931 ns | 0.5010 ns | 0.4687 ns | 246.754 ns | - | - | - | - | | Tagged_CounterIncrement_Enum | 5 | 86.906 ns | 0.2297 ns | 0.2149 ns | 86.819 ns | - | - | - | - | | Tagged_CounterIncrement_Enum_Many | 5 | 262.975 ns | 0.4159 ns | 0.3687 ns | 262.967 ns | - | - | - | - | | Method | iterations | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | |------------------------------------ |----------- |-----------:|----------:|----------:|------:|------:|------:|----------:| | CounterIncrement | ? | 4.116 ns | 0.0130 ns | 0.0122 ns | - | - | - | - | | SamplingSet | ? | 4.327 ns | 0.0024 ns | 0.0022 ns | - | - | - | - | | Tagged_CounterIncrement_String_One | 1 | 26.249 ns | 0.0504 ns | 0.0471 ns | - | - | - | - | | Tagged_CounterIncrement_String_Many | 1 | 76.369 ns | 0.0936 ns | 0.0875 ns | - | - | - | - | | Tagged_CounterIncrement_Int_One | 1 | 19.976 ns | 0.0493 ns | 0.0461 ns | - | - | - | - | | Tagged_CounterIncrement_Int_Many | 1 | 55.333 ns | 0.0478 ns | 0.0399 ns | - | - | - | - | | Tagged_CounterIncrement_Enum | 1 | 22.277 ns | 0.0465 ns | 0.0435 ns | - | - | - | - | | Tagged_CounterIncrement_Enum_Many | 1 | 65.830 ns | 0.0917 ns | 0.0813 ns | - | - | - | - | | Tagged_CounterIncrement_String_One | 2 | 52.486 ns | 0.1352 ns | 0.1198 ns | - | - | - | - | | Tagged_CounterIncrement_String_Many | 2 | 155.305 ns | 0.2501 ns | 0.2088 ns | - | - | - | - | | Tagged_CounterIncrement_Int_One | 2 | 37.659 ns | 0.1511 ns | 0.1340 ns | - | - | - | - | | Tagged_CounterIncrement_Int_Many | 2 | 109.224 ns | 0.2347 ns | 0.1960 ns | - | - | - | - | | Tagged_CounterIncrement_Enum | 2 | 42.445 ns | 0.0940 ns | 0.0833 ns | - | - | - | - | | Tagged_CounterIncrement_Enum_Many | 2 | 133.491 ns | 0.3657 ns | 0.3421 ns | - | - | - | - | | Tagged_CounterIncrement_String_One | 5 | 132.390 ns | 0.2045 ns | 0.1708 ns | - | - | - | - | | Tagged_CounterIncrement_String_Many | 5 | 380.167 ns | 0.3117 ns | 0.2763 ns | - | - | - | - | | Tagged_CounterIncrement_Int_One | 5 | 97.093 ns | 1.3728 ns | 1.2170 ns | - | - | - | - | | Tagged_CounterIncrement_Int_Many | 5 | 275.440 ns | 0.6153 ns | 0.5138 ns | - | - | - | - | | Tagged_CounterIncrement_Enum | 5 | 107.827 ns | 0.1076 ns | 0.0899 ns | - | - | - | - | | Tagged_CounterIncrement_Enum_Many | 5 | 321.891 ns | 0.4570 ns | 0.4051 ns | - | - | - | - | | Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | |------------------- |----------:|----------:|----------:|-------:|-------:|------:|----------:| | WriteReadings_NoOp | 1.680 us | 0.0055 us | 0.0052 us | 0.0553 | - | - | 464 B | | GetReadings_NoOp | 2.049 us | 0.0125 us | 0.0117 us | 0.2556 | - | - | 2152 B | | WriteReadings_Json | 16.128 us | 0.0658 us | 0.0615 us | 0.9460 | 0.0305 | - | 7896 B | | GetReadings_Json | 16.067 us | 0.0789 us | 0.0738 us | 1.1597 | 0.0305 | - | 9584 B | Note: `GetReadings_Json` captures readings into an `ImmutableArray` and writes to a JSON serializer (backed by a `BufferWriter`). `WriteReadings_Json` uses an optimized path used internally by the `MetricsCollector`, it also writes JSON. Allocations here are largely from JSON serialization! `WriteReadings_NoOp` and `GetReadings_NoOp` are the results without serialization involved! Co-authored-by: Dean Ward <dward@stackoverflow.com> Co-authored-by: Nick Craver <nrcraver@gmail.com> Co-authored-by: Nick Craver <craver@stackoverflow.com>
1 parent 44a676a commit 8f869ae

95 files changed

Lines changed: 6311 additions & 4137 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,4 @@ node_modules
168168
.idea
169169

170170
.vs
171+
BenchmarkDotNet.Artifacts

StackExchange.Metrics.sln

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ VisualStudioVersion = 16.0.29306.81
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StackExchange.Metrics", "src\StackExchange.Metrics\StackExchange.Metrics.csproj", "{9D6C3CB4-C01A-4335-8C69-347A335F24D1}"
77
EndProject
8-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Scratch", "tests\Scratch\Scratch.csproj", "{1CE60D86-9385-41FE-AEB8-427180FEFB6D}"
9-
EndProject
108
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{447B6C2F-9B2C-4325-8BEB-FB6B7A079A15}"
119
ProjectSection(SolutionItems) = preProject
1210
.gitattributes = .gitattributes
@@ -30,6 +28,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{EC09
3028
EndProject
3129
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StackExchange.Metrics.SampleHost", "samples\StackExchange.Metrics.SampleHost\StackExchange.Metrics.SampleHost.csproj", "{2D471133-028C-4346-B6A3-EC58B5643ECB}"
3230
EndProject
31+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{A20841A7-5CB3-46CE-A5F0-80D5CE68D3C4}"
32+
EndProject
33+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StackExchange.Metrics.Benchmarks", "benchmarks\StackExchange.Metrics.Benchmarks\StackExchange.Metrics.Benchmarks.csproj", "{FFED97CE-3EBC-4E4D-BAB3-8BFB5D6A3210}"
34+
EndProject
3335
Global
3436
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3537
Debug|Any CPU = Debug|Any CPU
@@ -40,10 +42,6 @@ Global
4042
{9D6C3CB4-C01A-4335-8C69-347A335F24D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
4143
{9D6C3CB4-C01A-4335-8C69-347A335F24D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
4244
{9D6C3CB4-C01A-4335-8C69-347A335F24D1}.Release|Any CPU.Build.0 = Release|Any CPU
43-
{1CE60D86-9385-41FE-AEB8-427180FEFB6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
44-
{1CE60D86-9385-41FE-AEB8-427180FEFB6D}.Debug|Any CPU.Build.0 = Debug|Any CPU
45-
{1CE60D86-9385-41FE-AEB8-427180FEFB6D}.Release|Any CPU.ActiveCfg = Release|Any CPU
46-
{1CE60D86-9385-41FE-AEB8-427180FEFB6D}.Release|Any CPU.Build.0 = Release|Any CPU
4745
{58B2EDE5-0EB4-46BC-BFB8-4B103F02E0A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
4846
{58B2EDE5-0EB4-46BC-BFB8-4B103F02E0A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
4947
{58B2EDE5-0EB4-46BC-BFB8-4B103F02E0A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -52,15 +50,19 @@ Global
5250
{2D471133-028C-4346-B6A3-EC58B5643ECB}.Debug|Any CPU.Build.0 = Debug|Any CPU
5351
{2D471133-028C-4346-B6A3-EC58B5643ECB}.Release|Any CPU.ActiveCfg = Release|Any CPU
5452
{2D471133-028C-4346-B6A3-EC58B5643ECB}.Release|Any CPU.Build.0 = Release|Any CPU
53+
{FFED97CE-3EBC-4E4D-BAB3-8BFB5D6A3210}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
54+
{FFED97CE-3EBC-4E4D-BAB3-8BFB5D6A3210}.Debug|Any CPU.Build.0 = Debug|Any CPU
55+
{FFED97CE-3EBC-4E4D-BAB3-8BFB5D6A3210}.Release|Any CPU.ActiveCfg = Release|Any CPU
56+
{FFED97CE-3EBC-4E4D-BAB3-8BFB5D6A3210}.Release|Any CPU.Build.0 = Release|Any CPU
5557
EndGlobalSection
5658
GlobalSection(SolutionProperties) = preSolution
5759
HideSolutionNode = FALSE
5860
EndGlobalSection
5961
GlobalSection(NestedProjects) = preSolution
6062
{9D6C3CB4-C01A-4335-8C69-347A335F24D1} = {D877841E-FCD9-4222-ACD7-7B7FE1C03778}
61-
{1CE60D86-9385-41FE-AEB8-427180FEFB6D} = {9D1086EB-5440-414D-87B6-E8318670A1F6}
6263
{58B2EDE5-0EB4-46BC-BFB8-4B103F02E0A5} = {9D1086EB-5440-414D-87B6-E8318670A1F6}
6364
{2D471133-028C-4346-B6A3-EC58B5643ECB} = {EC09332A-A10B-4DC4-9D6A-29BDDE8557E7}
65+
{FFED97CE-3EBC-4E4D-BAB3-8BFB5D6A3210} = {A20841A7-5CB3-46CE-A5F0-80D5CE68D3C4}
6466
EndGlobalSection
6567
GlobalSection(ExtensibilityGlobals) = postSolution
6668
SolutionGuid = {10BDE042-930B-4E4D-90F6-00358BAC6A52}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
using System.Collections.Generic;
2+
using BenchmarkDotNet.Attributes;
3+
using StackExchange.Metrics.Metrics;
4+
5+
namespace StackExchange.Metrics.Benchmarks
6+
{
7+
[Config(typeof(BenchmarkConfig))]
8+
public class CommonOps
9+
{
10+
public MetricSource _source;
11+
public Counter _counter;
12+
public SamplingGauge _samplingGauge;
13+
private Counter<string> _counterWithStringTag;
14+
private Counter<int> _counterWithIntTag;
15+
private Counter<MyEnum> _counterWithEnumTag;
16+
17+
public enum MyEnum
18+
{
19+
A, B, C
20+
};
21+
22+
23+
public IEnumerable<object[]> Iterations()
24+
{
25+
yield return new object[] { 1 };
26+
yield return new object[] { 2 };
27+
yield return new object[] { 5 };
28+
}
29+
30+
[GlobalSetup]
31+
public void Setup()
32+
{
33+
_source = new MetricSource(new MetricSourceOptions());
34+
_counter = _source.AddCounter("Test Counter", "Furlongs", "Invented by narwhals");
35+
_samplingGauge = _source.AddSamplingGauge("Test Gauge", "Furlongs", "Invented by narwhals");
36+
_counterWithStringTag = _source.AddCounter("Counter with Tags", "Furlongs", "Invented by narwhals", new MetricTag<string>("String"));
37+
_counterWithIntTag = _source.AddCounter("Counter with Tags", "Furlongs", "Invented by narwhals", new MetricTag<int>("Int"));
38+
_counterWithEnumTag = _source.AddCounter("Counter with Tags", "Furlongs", "Invented by narwhals", new MetricTag<MyEnum>("MyEnum"));
39+
}
40+
41+
[Benchmark]
42+
public void CounterIncrement() => _counter.Increment();
43+
44+
[Benchmark]
45+
public void SamplingSet() => _samplingGauge.Record(12);
46+
47+
[Benchmark]
48+
[ArgumentsSource(nameof(Iterations))]
49+
public void Tagged_CounterIncrement_String_One(int iterations)
50+
{
51+
for (var i = 0; i < iterations; i++)
52+
{
53+
_counterWithStringTag.Increment("A");
54+
}
55+
}
56+
57+
[Benchmark]
58+
[ArgumentsSource(nameof(Iterations))]
59+
public void Tagged_CounterIncrement_String_Many(int iterations)
60+
{
61+
for (var i = 0; i < iterations; i++)
62+
{
63+
_counterWithStringTag.Increment("A");
64+
_counterWithStringTag.Increment("B");
65+
_counterWithStringTag.Increment("C");
66+
}
67+
}
68+
69+
[Benchmark]
70+
[ArgumentsSource(nameof(Iterations))]
71+
public void Tagged_CounterIncrement_Int_One(int iterations)
72+
{
73+
for (var i = 0; i < iterations; i++)
74+
{
75+
_counterWithIntTag.Increment(1);
76+
}
77+
}
78+
79+
[Benchmark]
80+
[ArgumentsSource(nameof(Iterations))]
81+
public void Tagged_CounterIncrement_Int_Many(int iterations)
82+
{
83+
for (var i = 0; i < iterations; i++)
84+
{
85+
_counterWithIntTag.Increment(1);
86+
_counterWithIntTag.Increment(2);
87+
_counterWithIntTag.Increment(3);
88+
}
89+
}
90+
91+
[Benchmark]
92+
[ArgumentsSource(nameof(Iterations))]
93+
public void Tagged_CounterIncrement_Enum(int iterations)
94+
{
95+
for (var i = 0; i < iterations; i++)
96+
{
97+
_counterWithEnumTag.Increment(MyEnum.A);
98+
}
99+
}
100+
101+
[Benchmark]
102+
[ArgumentsSource(nameof(Iterations))]
103+
public void Tagged_CounterIncrement_Enum_Many(int iterations)
104+
{
105+
for (var i = 0; i < iterations; i++)
106+
{
107+
_counterWithEnumTag.Increment(MyEnum.A);
108+
_counterWithEnumTag.Increment(MyEnum.B);
109+
_counterWithEnumTag.Increment(MyEnum.C);
110+
}
111+
}
112+
}
113+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using BenchmarkDotNet.Configs;
2+
using BenchmarkDotNet.Diagnosers;
3+
4+
namespace StackExchange.Metrics.Benchmarks
5+
{
6+
/// <summary>
7+
/// Shared BenchmarkDotNet configuration for all benchmarks.
8+
/// </summary>
9+
public class BenchmarkConfig : ManualConfig
10+
{
11+
public BenchmarkConfig()
12+
{
13+
//Job.Default.WithRuntime(CoreRuntime.Core31);
14+
AddDiagnoser(MemoryDiagnoser.Default);
15+
}
16+
}
17+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using BenchmarkDotNet.Running;
2+
3+
namespace StackExchange.Metrics.Benchmarks
4+
{
5+
class Program
6+
{
7+
static void Main(string[] args) => BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
8+
}
9+
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
using System;
2+
using System.Text.Json;
3+
using BenchmarkDotNet.Attributes;
4+
using Pipelines.Sockets.Unofficial.Buffers;
5+
using StackExchange.Metrics.Infrastructure;
6+
using StackExchange.Metrics.Metrics;
7+
8+
namespace StackExchange.Metrics.Benchmarks
9+
{
10+
[Config(typeof(BenchmarkConfig))]
11+
public class Readings
12+
{
13+
private BenchmarkSource[] _sources;
14+
private BufferWriter<byte> _buffer;
15+
private BufferWriterBatch _bufferBatch;
16+
private NoOpBatch _noOpBatch;
17+
18+
private class BenchmarkSource : MetricSource
19+
{
20+
public Counter Counter { get; }
21+
public SamplingGauge Gauge { get; }
22+
public Counter<string> CounterWithTag { get; }
23+
public BenchmarkSource(MetricSourceOptions options) : base(options)
24+
{
25+
Counter = AddCounter("Test Counter", "Furlongs", "Invented by narwhals");
26+
Gauge = AddSamplingGauge("Test Gauge", "Furlongs", "Invented by narwhals");
27+
CounterWithTag = AddCounter("Counter with Tags", "Furlongs", "Invented by narwhals", new MetricTag<string>("String"));
28+
}
29+
}
30+
31+
[GlobalSetup]
32+
public void Setup()
33+
{
34+
var options = new MetricSourceOptions();
35+
36+
_buffer = BufferWriter<byte>.Create(8192);
37+
_bufferBatch = new BufferWriterBatch(_buffer);
38+
_noOpBatch = new NoOpBatch();
39+
_sources = new[]
40+
{
41+
new BenchmarkSource(options),
42+
new BenchmarkSource(options),
43+
new BenchmarkSource(options)
44+
};
45+
46+
for (var i = 0; i < _sources.Length; i++)
47+
{
48+
_sources[i].CounterWithTag.Increment("A");
49+
_sources[i].CounterWithTag.Increment("B");
50+
_sources[i].CounterWithTag.Increment("C");
51+
}
52+
}
53+
54+
[GlobalCleanup]
55+
public void Cleanup()
56+
{
57+
_buffer.Dispose();
58+
}
59+
60+
[Benchmark]
61+
public void WriteReadings_NoOp()
62+
{
63+
for (var i = 0; i < _sources.Length; i++)
64+
{
65+
_sources[i].Counter.Increment();
66+
_sources[i].Gauge.Record(10);
67+
_sources[i].CounterWithTag.Increment("A");
68+
_sources[i].CounterWithTag.Increment("B");
69+
_sources[i].CounterWithTag.Increment("C");
70+
}
71+
72+
_sources.WriteReadings(_noOpBatch, DateTime.UtcNow);
73+
using (_buffer.Flush())
74+
{
75+
}
76+
}
77+
78+
[Benchmark]
79+
public void GetReadings_NoOp()
80+
{
81+
for (var i = 0; i < _sources.Length; i++)
82+
{
83+
_sources[i].Counter.Increment();
84+
_sources[i].Gauge.Record(10);
85+
_sources[i].CounterWithTag.Increment("A");
86+
_sources[i].CounterWithTag.Increment("B");
87+
_sources[i].CounterWithTag.Increment("C");
88+
}
89+
90+
_ = _sources.GetReadings(DateTime.UtcNow);
91+
}
92+
93+
[Benchmark]
94+
public void WriteReadings_Json()
95+
{
96+
for (var i = 0; i < _sources.Length; i++)
97+
{
98+
_sources[i].Counter.Increment();
99+
_sources[i].Gauge.Record(10);
100+
_sources[i].CounterWithTag.Increment("A");
101+
_sources[i].CounterWithTag.Increment("B");
102+
_sources[i].CounterWithTag.Increment("C");
103+
}
104+
105+
_sources.WriteReadings(_bufferBatch, DateTime.UtcNow);
106+
using (_buffer.Flush( ))
107+
{
108+
}
109+
}
110+
111+
[Benchmark]
112+
public void GetReadings_Json()
113+
{
114+
for (var i = 0; i < _sources.Length; i++)
115+
{
116+
_sources[i].Counter.Increment();
117+
_sources[i].Gauge.Record(10);
118+
_sources[i].CounterWithTag.Increment("A");
119+
_sources[i].CounterWithTag.Increment("B");
120+
_sources[i].CounterWithTag.Increment("C");
121+
}
122+
123+
var readings = _sources.GetReadings(DateTime.UtcNow);
124+
foreach (var reading in readings)
125+
{
126+
using (var utfWriter = new Utf8JsonWriter(_buffer))
127+
{
128+
JsonSerializer.Serialize(utfWriter, reading);
129+
}
130+
}
131+
using (_buffer.Flush())
132+
{
133+
}
134+
}
135+
136+
private class NoOpBatch : IMetricReadingBatch
137+
{
138+
public long BytesWritten => 0;
139+
140+
public long MetricsWritten => 0;
141+
142+
public void Add(in MetricReading reading)
143+
{
144+
}
145+
}
146+
147+
private class BufferWriterBatch : IMetricReadingBatch
148+
{
149+
private readonly BufferWriter<byte> _buffer;
150+
151+
public BufferWriterBatch(BufferWriter<byte> buffer)
152+
{
153+
_buffer = buffer;
154+
}
155+
156+
public long BytesWritten => 0;
157+
158+
public long MetricsWritten => 0;
159+
160+
public void Add(in MetricReading reading)
161+
{
162+
using (var utfWriter = new Utf8JsonWriter(_buffer))
163+
{
164+
JsonSerializer.Serialize(utfWriter, reading);
165+
}
166+
}
167+
}
168+
}
169+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFrameworks>net472;netcoreapp3.1</TargetFrameworks>
6+
<IsPackable>false</IsPackable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<ProjectReference Include="..\..\src\StackExchange.Metrics\StackExchange.Metrics.csproj" />
11+
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
12+
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="all" IncludeAssets="runtime;build;native;contentfiles;analyzers" />
13+
</ItemGroup>
14+
15+
</Project>

global.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"sdk": {
3+
"version": "3.1.100",
4+
"rollForward": "latestMajor",
5+
"allowPrerelease": false
6+
}
7+
}

0 commit comments

Comments
 (0)