Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/OpenTelemetry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

* Update `AggregatorStore` to reclaim unused MetricPoints for Delta aggregation
temporality.
([#4486](https://github.com/open-telemetry/opentelemetry-dotnet/issues/4486))

## 1.6.0

Released 2023-Sep-05
Expand Down
621 changes: 612 additions & 9 deletions src/OpenTelemetry/Metrics/AggregatorStore.cs

Large diffs are not rendered by default.

31 changes: 31 additions & 0 deletions src/OpenTelemetry/Metrics/LookupData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// <copyright file="LookupData.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

namespace OpenTelemetry.Metrics;

internal sealed class LookupData
{
public int Index;
public Tags SortedTags;
public Tags GivenTags;

public LookupData(int index, in Tags sortedTags, in Tags givenTags)
{
this.Index = index;
this.SortedTags = sortedTags;
this.GivenTags = givenTags;
}
}
42 changes: 41 additions & 1 deletion src/OpenTelemetry/Metrics/MetricPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ namespace OpenTelemetry.Metrics;
/// </summary>
public struct MetricPoint
{
// Represents the number of update threads using this MetricPoint at any given point of time.
// If the value is equal to int.MinValue which is -2147483648, it means that this MetricPoint is available for reuse.
// We never increment the ReferenceCount for MetricPoint with no tags (index == 0) and the MetricPoint for overflow attribute,
// but we always decrement it (in the Update methods). This should be fine.
// ReferenceCount doesn't matter for MetricPoint with no tags and overflow attribute as they are never reclaimed.
internal int ReferenceCount;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alanwest @vishweshbankwar This is going to add to our memory consumption (4 bytes x MaxMetricPoints). Below there is another field AggregationType aggType which I'm guessing is also taking up 4 bytes. I don't think either of those two things really need the full 4 bytes, what if we did some bit manipulation on a single int field to process both needs with better memory utilization?


// When the AggregatorStore is reclaiming MetricPoints, this serves the purpose of validating the a given thread is using the right
// MetricPoint for update by checking it against what as added in the Dictionary. Also, when a thread finds out that the MetricPoint
// that its using is already reclaimed, this helps avoid sorting of the tags for adding a new Dictionary entry.
internal LookupData? LookupData;

private const int DefaultSimpleReservoirPoolSize = 1;

private readonly AggregatorStore aggregatorStore;
Expand All @@ -46,17 +58,25 @@ internal MetricPoint(
KeyValuePair<string, object?>[]? tagKeysAndValues,
double[] histogramExplicitBounds,
int exponentialHistogramMaxSize,
int exponentialHistogramMaxScale)
int exponentialHistogramMaxScale,
LookupData? lookupData = null)
{
Debug.Assert(aggregatorStore != null, "AggregatorStore was null.");
Debug.Assert(histogramExplicitBounds != null, "Histogram explicit Bounds was null.");

if (aggregatorStore!.OutputDelta)
{
Debug.Assert(lookupData != null, "LookupData was null.");
}

this.aggType = aggType;
this.Tags = new ReadOnlyTagCollection(tagKeysAndValues);
this.runningValue = default;
this.snapshotValue = default;
this.deltaLastValue = default;
this.MetricPointStatus = MetricPointStatus.NoCollectPending;
this.ReferenceCount = 1;
this.LookupData = lookupData;

ExemplarReservoir? reservoir = null;
if (this.aggType == AggregationType.HistogramWithBuckets ||
Expand Down Expand Up @@ -437,6 +457,11 @@ internal void Update(long number)
// TODO: For Delta, this can be mitigated
// by ignoring Zero points
this.MetricPointStatus = MetricPointStatus.CollectPending;

if (this.aggregatorStore.OutputDelta)
{
Interlocked.Decrement(ref this.ReferenceCount);
}
}

internal void UpdateWithExemplar(long number, ReadOnlySpan<KeyValuePair<string, object?>> tags, bool isSampled)
Expand Down Expand Up @@ -551,6 +576,11 @@ internal void UpdateWithExemplar(long number, ReadOnlySpan<KeyValuePair<string,
// TODO: For Delta, this can be mitigated
// by ignoring Zero points
this.MetricPointStatus = MetricPointStatus.CollectPending;

if (this.aggregatorStore.OutputDelta)
{
Interlocked.Decrement(ref this.ReferenceCount);
}
}

internal void Update(double number)
Expand Down Expand Up @@ -642,6 +672,11 @@ internal void Update(double number)
// TODO: For Delta, this can be mitigated
// by ignoring Zero points
this.MetricPointStatus = MetricPointStatus.CollectPending;

if (this.aggregatorStore.OutputDelta)
{
Interlocked.Decrement(ref this.ReferenceCount);
}
}

internal void UpdateWithExemplar(double number, ReadOnlySpan<KeyValuePair<string, object?>> tags, bool isSampled)
Expand Down Expand Up @@ -762,6 +797,11 @@ internal void UpdateWithExemplar(double number, ReadOnlySpan<KeyValuePair<string
// TODO: For Delta, this can be mitigated
// by ignoring Zero points
this.MetricPointStatus = MetricPointStatus.CollectPending;

if (this.aggregatorStore.OutputDelta)
{
Interlocked.Decrement(ref this.ReferenceCount);
}
}

internal void TakeSnapshot(bool outputDelta)
Expand Down
2 changes: 2 additions & 0 deletions src/OpenTelemetry/Metrics/Tags.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ namespace OpenTelemetry.Metrics;

internal readonly struct Tags : IEquatable<Tags>
{
public static readonly Tags EmptyTags = new(Array.Empty<KeyValuePair<string, object?>>());

private readonly int hashCode;

public Tags(KeyValuePair<string, object?>[] keyValuePairs)
Expand Down
32 changes: 16 additions & 16 deletions test/Benchmarks/Metrics/MetricsBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,27 +22,27 @@
using OpenTelemetry.Tests;

/*
BenchmarkDotNet=v0.13.5, OS=Windows 11 (10.0.23424.1000)
BenchmarkDotNet v0.13.6, Windows 11 (10.0.23424.1000)
Intel Core i7-9700 CPU 3.00GHz, 1 CPU, 8 logical and 8 physical cores
.NET SDK=7.0.203
[Host] : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2
DefaultJob : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2
.NET SDK 7.0.400
[Host] : .NET 7.0.10 (7.0.1023.36312), X64 RyuJIT AVX2
DefaultJob : .NET 7.0.10 (7.0.1023.36312), X64 RyuJIT AVX2


| Method | AggregationTemporality | Mean | Error | StdDev | Allocated |
|-------------------------- |----------------------- |----------:|---------:|---------:|----------:|
| CounterHotPath | Cumulative | 17.06 ns | 0.113 ns | 0.094 ns | - |
| CounterWith1LabelsHotPath | Cumulative | 71.47 ns | 1.464 ns | 2.100 ns | - |
| CounterWith3LabelsHotPath | Cumulative | 162.04 ns | 2.469 ns | 2.188 ns | - |
| CounterWith5LabelsHotPath | Cumulative | 237.30 ns | 2.884 ns | 2.698 ns | - |
| CounterWith6LabelsHotPath | Cumulative | 269.41 ns | 4.087 ns | 3.623 ns | - |
| CounterWith7LabelsHotPath | Cumulative | 303.01 ns | 5.313 ns | 4.970 ns | - |
| CounterHotPath | Delta | 17.30 ns | 0.350 ns | 0.310 ns | - |
| CounterWith1LabelsHotPath | Delta | 70.96 ns | 0.608 ns | 0.539 ns | - |
| CounterWith3LabelsHotPath | Delta | 156.55 ns | 3.139 ns | 3.358 ns | - |
| CounterWith5LabelsHotPath | Delta | 247.14 ns | 4.703 ns | 5.598 ns | - |
| CounterWith6LabelsHotPath | Delta | 271.30 ns | 5.310 ns | 5.215 ns | - |
| CounterWith7LabelsHotPath | Delta | 309.02 ns | 5.934 ns | 5.828 ns | - |
| CounterHotPath | Cumulative | 21.61 ns | 0.084 ns | 0.078 ns | - |
| CounterWith1LabelsHotPath | Cumulative | 69.08 ns | 0.261 ns | 0.244 ns | - |
| CounterWith3LabelsHotPath | Cumulative | 149.77 ns | 0.549 ns | 0.486 ns | - |
| CounterWith5LabelsHotPath | Cumulative | 236.47 ns | 1.684 ns | 1.493 ns | - |
| CounterWith6LabelsHotPath | Cumulative | 276.48 ns | 1.442 ns | 1.349 ns | - |
| CounterWith7LabelsHotPath | Cumulative | 294.09 ns | 2.354 ns | 2.202 ns | - |
| CounterHotPath | Delta | 27.32 ns | 0.380 ns | 0.355 ns | - |
| CounterWith1LabelsHotPath | Delta | 80.83 ns | 0.219 ns | 0.183 ns | - |
| CounterWith3LabelsHotPath | Delta | 162.48 ns | 1.053 ns | 0.985 ns | - |
| CounterWith5LabelsHotPath | Delta | 255.48 ns | 1.807 ns | 1.602 ns | - |
| CounterWith6LabelsHotPath | Delta | 281.75 ns | 2.761 ns | 2.583 ns | - |
| CounterWith7LabelsHotPath | Delta | 310.29 ns | 1.817 ns | 1.611 ns | - |
*/

namespace Benchmarks.Metrics;
Expand Down
Loading