-
Notifications
You must be signed in to change notification settings - Fork 889
[OpenTelemetry] Optimize trace sampling #7057
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
eb262cb
8213721
2ec341e
de0e117
b800efc
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 |
|---|---|---|
|
|
@@ -222,7 +222,7 @@ internal TracerProviderSdk( | |
|
|
||
| if (this.Sampler is AlwaysOnSampler) | ||
| { | ||
| activityListener.Sample = (ref options) => | ||
|
martincostello marked this conversation as resolved.
|
||
| activityListener.Sample = static (ref _) => | ||
| !Sdk.SuppressInstrumentation ? ActivitySamplingResult.AllDataAndRecorded : ActivitySamplingResult.None; | ||
| this.getRequestedDataAction = this.RunGetRequestedDataAlwaysOnSampler; | ||
| } | ||
|
|
@@ -479,9 +479,12 @@ private static ActivitySamplingResult ComputeActivitySamplingResult( | |
|
|
||
| if (activitySamplingResult > ActivitySamplingResult.PropagationData) | ||
| { | ||
| foreach (var att in samplingResult.Attributes) | ||
| if (samplingResult.AttributesOrNull is { } attributes) | ||
|
Member
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. ❤️ nice elimination of the alloc here!
Member
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.
Member
Author
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. Not sure - but the compiler can use special private types for collection expressions so "not null" is probably best than assuming it's the exact object returned by |
||
| { | ||
| options.SamplingTags.Add(att.Key, att.Value); | ||
| foreach (var att in attributes) | ||
| { | ||
| options.SamplingTags.Add(att.Key, att.Value); | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -575,9 +578,12 @@ private void RunGetRequestedDataOtherSampler(Activity activity) | |
|
|
||
| if (samplingResult.Decision != SamplingDecision.Drop) | ||
| { | ||
| foreach (var att in samplingResult.Attributes) | ||
| if (samplingResult.AttributesOrNull is { } attributes) | ||
| { | ||
| activity.SetTag(att.Key, att.Value); | ||
| foreach (var att in attributes) | ||
| { | ||
| activity.SetTag(att.Key, att.Value); | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,134 @@ | ||
| // Copyright The OpenTelemetry Authors | ||
|
Member
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. nit: could we put in the same
Member
Author
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. Sorry, I don't quite follow this comment. Do you mean to just move the new benchmarks into |
||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| using System.Diagnostics; | ||
| using BenchmarkDotNet.Attributes; | ||
| using OpenTelemetry; | ||
| using OpenTelemetry.Trace; | ||
|
|
||
| namespace Benchmarks.Trace; | ||
|
|
||
| #pragma warning disable CA1001 // Types that own disposable fields should be disposable - handled by GlobalCleanup | ||
| [MemoryDiagnoser] | ||
| public class SamplingResultBenchmarks | ||
| #pragma warning restore CA1001 // Types that own disposable fields should be disposable - handled by GlobalCleanup | ||
| { | ||
| private static readonly KeyValuePair<string, object>[] SamplingAttributes = | ||
| [ | ||
| new("sampling.priority", 1), | ||
| new("sampling.rule", "always"), | ||
| ]; | ||
|
|
||
| private ActivitySource? sourceNoAttributes; | ||
| private ActivitySource? sourceWithAttributeArray; | ||
| private ActivitySource? sourceWithAttributeList; | ||
| private ActivitySource? sourceDrop; | ||
| private ActivitySource? sourceParentBased; | ||
|
|
||
| private ActivityContext sampledRemoteParent; | ||
|
|
||
| private TracerProvider? providerNoAttributes; | ||
| private TracerProvider? providerWithAttributeArray; | ||
| private TracerProvider? providerWithAttributeList; | ||
| private TracerProvider? providerDrop; | ||
| private TracerProvider? providerParentBased; | ||
|
|
||
| [GlobalSetup] | ||
| public void Setup() | ||
| { | ||
| this.sourceNoAttributes = new ActivitySource("SamplingResult.NoAttributes"); | ||
| this.sourceWithAttributeArray = new ActivitySource("SamplingResult.WithAttributeArray"); | ||
| this.sourceWithAttributeList = new ActivitySource("SamplingResult.WithAttributeList"); | ||
| this.sourceDrop = new ActivitySource("SamplingResult.Drop"); | ||
| this.sourceParentBased = new ActivitySource("SamplingResult.ParentBased"); | ||
|
|
||
| this.sampledRemoteParent = new ActivityContext( | ||
| ActivityTraceId.CreateRandom(), | ||
| ActivitySpanId.CreateRandom(), | ||
| ActivityTraceFlags.Recorded, | ||
| traceState: null, | ||
| isRemote: true); | ||
|
|
||
| // Sampler returns RecordAndSample with no attributes - the common case. | ||
| this.providerNoAttributes = Sdk.CreateTracerProviderBuilder() | ||
| .AddSource(this.sourceNoAttributes.Name) | ||
| .SetSampler(new DelegateSampler(_ => new SamplingResult(SamplingDecision.RecordAndSample))) | ||
| .Build(); | ||
|
|
||
| // Sampler returns attributes as a T[] - exercises the array fast-path. | ||
| this.providerWithAttributeArray = Sdk.CreateTracerProviderBuilder() | ||
| .AddSource(this.sourceWithAttributeArray.Name) | ||
| .SetSampler(new DelegateSampler(_ => new SamplingResult(SamplingDecision.RecordAndSample, SamplingAttributes))) | ||
| .Build(); | ||
|
|
||
| // Sampler returns attributes as a List<T> - exercises the IEnumerable fallback path. | ||
| this.providerWithAttributeList = Sdk.CreateTracerProviderBuilder() | ||
| .AddSource(this.sourceWithAttributeList.Name) | ||
| .SetSampler(new DelegateSampler(_ => new SamplingResult(SamplingDecision.RecordAndSample, [.. SamplingAttributes]))) | ||
| .Build(); | ||
|
|
||
| // Sampler drops the span - attribute loop is never entered. | ||
| this.providerDrop = Sdk.CreateTracerProviderBuilder() | ||
| .AddSource(this.sourceDrop.Name) | ||
| .SetSampler(new DelegateSampler(_ => new SamplingResult(SamplingDecision.Drop))) | ||
| .Build(); | ||
|
|
||
| // ParentBasedSampler with AlwaysOnSampler root - realistic production default. | ||
| this.providerParentBased = Sdk.CreateTracerProviderBuilder() | ||
| .AddSource(this.sourceParentBased.Name) | ||
| .SetSampler(new ParentBasedSampler(new AlwaysOnSampler())) | ||
| .Build(); | ||
| } | ||
|
|
||
| [GlobalCleanup] | ||
| public void Cleanup() | ||
| { | ||
| this.sourceNoAttributes?.Dispose(); | ||
| this.sourceWithAttributeArray?.Dispose(); | ||
| this.sourceWithAttributeList?.Dispose(); | ||
| this.sourceDrop?.Dispose(); | ||
| this.sourceParentBased?.Dispose(); | ||
|
|
||
| this.providerNoAttributes?.Dispose(); | ||
| this.providerWithAttributeArray?.Dispose(); | ||
| this.providerWithAttributeList?.Dispose(); | ||
| this.providerDrop?.Dispose(); | ||
| this.providerParentBased?.Dispose(); | ||
| } | ||
|
|
||
| [Benchmark(Baseline = true)] | ||
| public void NoAttributes() | ||
| { | ||
| using var activity = this.sourceNoAttributes!.StartActivity("Benchmark", ActivityKind.Server, this.sampledRemoteParent); | ||
| } | ||
|
|
||
| [Benchmark] | ||
| public void WithAttributeArray() | ||
| { | ||
| using var activity = this.sourceWithAttributeArray!.StartActivity("Benchmark", ActivityKind.Server, this.sampledRemoteParent); | ||
| } | ||
|
|
||
| [Benchmark] | ||
| public void WithAttributeList() | ||
| { | ||
| using var activity = this.sourceWithAttributeList!.StartActivity("Benchmark", ActivityKind.Server, this.sampledRemoteParent); | ||
| } | ||
|
|
||
| [Benchmark] | ||
| public void Drop() | ||
| { | ||
| using var activity = this.sourceDrop!.StartActivity("Benchmark", ActivityKind.Server, this.sampledRemoteParent); | ||
| } | ||
|
|
||
| [Benchmark] | ||
| public void ParentBasedSampled() | ||
| { | ||
| using var activity = this.sourceParentBased!.StartActivity("Benchmark", ActivityKind.Server, this.sampledRemoteParent); | ||
| } | ||
|
|
||
| private sealed class DelegateSampler(Func<SamplingParameters, SamplingResult> sample) : Sampler | ||
| { | ||
| public override SamplingResult ShouldSample(in SamplingParameters samplingParameters) | ||
| => sample(samplingParameters); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.