Skip to content
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

Replace recordingSpan attributes implementation with map of attributes #2555

Closed
wants to merge 16 commits into from

Conversation

MrAlias
Copy link
Contributor

@MrAlias MrAlias commented Jan 26, 2022

Instead of an LRU strategy for cap-ing span attributes, comply with the specification and drop last added. This means the attributesMap type can be replaced with a simple map.

Resolves #2554 and relates to #2402

Testing Performance

This looks to have considerable memory and allocation reduction and faster computations.

$ benchstat bench-main-53ead308 bench-branch

name                                          old time/op    new time/op    delta
SpanProcessor-8                                 14.9µs ±10%    12.6µs ± 9%  -15.45%  (p=0.000 n=8+10)
SpanProcessorVerboseLogging-8                   15.5µs ± 3%    12.8µs ±16%  -17.45%  (p=0.000 n=8+10)
StartEndSpan/AlwaysSample-8                      978ns ±14%     708ns ±24%  -27.66%  (p=0.000 n=10+10)
StartEndSpan/NeverSample-8                       367ns ± 2%     312ns ± 9%  -15.14%  (p=0.000 n=8+9)
SpanSetAttributesOverCapacity-8                 23.1µs ± 5%     2.3µs ±13%  -89.97%  (p=0.000 n=10+9)
SpanWithAttributes_4/AlwaysSample-8             1.85µs ± 5%    1.28µs ±11%  -31.00%  (p=0.000 n=8+9)
SpanWithAttributes_4/NeverSample-8               513ns ± 7%     481ns ± 5%   -6.25%  (p=0.004 n=9+9)
SpanWithAttributes_8/AlwaysSample-8             2.68µs ± 5%    1.60µs ± 5%  -40.35%  (p=0.000 n=8+9)
SpanWithAttributes_8/NeverSample-8               679ns ± 5%     644ns ± 7%   -5.15%  (p=0.021 n=8+10)
SpanWithAttributes_all/AlwaysSample-8           2.09µs ± 7%    1.36µs ± 3%  -35.29%  (p=0.000 n=9+9)
SpanWithAttributes_all/NeverSample-8             566ns ± 5%     508ns ± 7%  -10.36%  (p=0.000 n=8+9)
SpanWithAttributes_all_2x/AlwaysSample-8        3.72µs ±12%    2.57µs ± 9%  -30.98%  (p=0.000 n=10+9)
SpanWithAttributes_all_2x/NeverSample-8          753ns ± 6%     690ns ± 8%   -8.47%  (p=0.001 n=8+9)
SpanWithEvents_4/AlwaysSample-8                 2.04µs ± 7%    1.55µs ± 9%  -23.98%  (p=0.000 n=10+9)
SpanWithEvents_4/NeverSample-8                   386ns ± 3%     324ns ± 7%  -16.00%  (p=0.000 n=9+9)
SpanWithEvents_8/AlwaysSample-8                 3.02µs ± 3%    2.32µs ± 8%  -23.28%  (p=0.000 n=9+8)
SpanWithEvents_8/NeverSample-8                   395ns ± 8%     339ns ± 7%  -14.04%  (p=0.000 n=10+10)
SpanWithEvents_WithStackTrace/AlwaysSample-8    1.26µs ± 8%    1.01µs ±12%  -20.06%  (p=0.000 n=10+9)
SpanWithEvents_WithStackTrace/NeverSample-8      423ns ±10%     378ns ±16%  -10.57%  (p=0.008 n=9+10)
SpanWithEvents_WithTimestamp/AlwaysSample-8     1.24µs ± 5%    0.98µs ± 9%  -21.43%  (p=0.000 n=10+9)
SpanWithEvents_WithTimestamp/NeverSample-8       470ns ± 2%     418ns ± 8%  -11.07%  (p=0.000 n=10+10)
TraceID_DotString-8                             82.2ns ± 5%    84.2ns ±10%     ~     (p=0.278 n=10+9)
SpanID_DotString-8                              62.3ns ± 2%    62.7ns ± 6%     ~     (p=0.859 n=10+9)

name                                          old alloc/op   new alloc/op   delta
SpanProcessor-8                                 12.3kB ± 0%     8.6kB ± 0%  -29.99%  (p=0.000 n=10+10)
SpanProcessorVerboseLogging-8                   13.4kB ± 0%     9.7kB ± 0%  -27.43%  (p=0.000 n=10+9)
StartEndSpan/AlwaysSample-8                       816B ± 0%      448B ± 0%  -45.10%  (p=0.000 n=10+10)
StartEndSpan/NeverSample-8                        224B ± 0%      128B ± 0%  -42.86%  (p=0.000 n=10+10)
SpanSetAttributesOverCapacity-8                 15.4kB ± 0%     1.0kB ± 0%  -93.33%  (p=0.000 n=10+10)
SpanWithAttributes_4/AlwaysSample-8             1.73kB ± 0%    1.28kB ± 0%  -25.93%  (p=0.000 n=10+10)
SpanWithAttributes_4/NeverSample-8                480B ± 0%      384B ± 0%  -20.00%  (p=0.000 n=10+10)
SpanWithAttributes_8/AlwaysSample-8             2.43kB ± 0%    1.54kB ± 0%  -36.84%  (p=0.000 n=10+10)
SpanWithAttributes_8/NeverSample-8                736B ± 0%      640B ± 0%  -13.04%  (p=0.000 n=10+10)
SpanWithAttributes_all/AlwaysSample-8           1.90kB ± 0%    1.34kB ± 0%  -29.41%  (p=0.000 n=10+10)
SpanWithAttributes_all/NeverSample-8              544B ± 0%      448B ± 0%  -17.65%  (p=0.000 n=10+10)
SpanWithAttributes_all_2x/AlwaysSample-8        3.20kB ± 0%    2.83kB ± 0%  -11.74%  (p=0.000 n=10+10)
SpanWithAttributes_all_2x/NeverSample-8           864B ± 0%      768B ± 0%  -11.11%  (p=0.000 n=10+10)
SpanWithEvents_4/AlwaysSample-8                 1.50kB ± 0%    0.88kB ± 0%  -41.49%  (p=0.000 n=10+10)
SpanWithEvents_4/NeverSample-8                    224B ± 0%      128B ± 0%  -42.86%  (p=0.000 n=10+10)
SpanWithEvents_8/AlwaysSample-8                 2.21kB ± 0%    1.33kB ± 0%  -39.86%  (p=0.000 n=10+10)
SpanWithEvents_8/NeverSample-8                    224B ± 0%      128B ± 0%  -42.86%  (p=0.000 n=10+10)
SpanWithEvents_WithStackTrace/AlwaysSample-8      992B ± 0%      560B ± 0%  -43.55%  (p=0.000 n=10+10)
SpanWithEvents_WithStackTrace/NeverSample-8       240B ± 0%      144B ± 0%  -40.00%  (p=0.000 n=10+10)
SpanWithEvents_WithTimestamp/AlwaysSample-8     1.02kB ± 0%    0.58kB ± 0%  -42.52%  (p=0.000 n=10+10)
SpanWithEvents_WithTimestamp/NeverSample-8        264B ± 0%      168B ± 0%  -36.36%  (p=0.000 n=10+10)

name                                          old allocs/op  new allocs/op  delta
SpanProcessor-8                                   85.0 ± 0%      45.0 ± 0%  -47.06%  (p=0.000 n=10+10)
SpanProcessorVerboseLogging-8                     91.0 ± 0%      51.0 ± 0%  -43.96%  (p=0.000 n=10+9)
StartEndSpan/AlwaysSample-8                       7.00 ± 0%      3.00 ± 0%  -57.14%  (p=0.000 n=10+10)
StartEndSpan/NeverSample-8                        3.00 ± 0%      2.00 ± 0%  -33.33%  (p=0.000 n=10+10)
SpanSetAttributesOverCapacity-8                    264 ± 0%         4 ± 0%  -98.48%  (p=0.000 n=10+10)
SpanWithAttributes_4/AlwaysSample-8               17.0 ± 0%       5.0 ± 0%  -70.59%  (p=0.000 n=10+10)
SpanWithAttributes_4/NeverSample-8                4.00 ± 0%      3.00 ± 0%  -25.00%  (p=0.000 n=10+10)
SpanWithAttributes_8/AlwaysSample-8               25.0 ± 0%       5.0 ± 0%  -80.00%  (p=0.000 n=10+10)
SpanWithAttributes_8/NeverSample-8                4.00 ± 0%      3.00 ± 0%  -25.00%  (p=0.000 n=10+10)
SpanWithAttributes_all/AlwaysSample-8             19.0 ± 0%       5.0 ± 0%  -73.68%  (p=0.000 n=10+10)
SpanWithAttributes_all/NeverSample-8              4.00 ± 0%      3.00 ± 0%  -25.00%  (p=0.000 n=10+10)
SpanWithAttributes_all_2x/AlwaysSample-8          30.0 ± 0%       6.0 ± 0%  -80.00%  (p=0.000 n=10+10)
SpanWithAttributes_all_2x/NeverSample-8           4.00 ± 0%      3.00 ± 0%  -25.00%  (p=0.000 n=10+10)
SpanWithEvents_4/AlwaysSample-8                   18.0 ± 0%      10.0 ± 0%  -44.44%  (p=0.000 n=10+10)
SpanWithEvents_4/NeverSample-8                    3.00 ± 0%      2.00 ± 0%  -33.33%  (p=0.000 n=10+10)
SpanWithEvents_8/AlwaysSample-8                   27.0 ± 0%      15.0 ± 0%  -44.44%  (p=0.000 n=10+10)
SpanWithEvents_8/NeverSample-8                    3.00 ± 0%      2.00 ± 0%  -33.33%  (p=0.000 n=10+10)
SpanWithEvents_WithStackTrace/AlwaysSample-8      11.0 ± 0%       6.0 ± 0%  -45.45%  (p=0.000 n=10+10)
SpanWithEvents_WithStackTrace/NeverSample-8       4.00 ± 0%      3.00 ± 0%  -25.00%  (p=0.000 n=10+10)
SpanWithEvents_WithTimestamp/AlwaysSample-8       12.0 ± 0%       7.0 ± 0%  -41.67%  (p=0.000 n=10+10)
SpanWithEvents_WithTimestamp/NeverSample-8        5.00 ± 0%      4.00 ± 0%  -20.00%  (p=0.000 n=10+10)

Instead of an LRU strategy for cap-ing span attributes, comply with the
specification and drop last added. This means the attributesmap type can
be replaced with a simple map.
@MrAlias MrAlias added bug Something isn't working area:trace Part of OpenTelemetry tracing labels Jan 26, 2022
@MrAlias MrAlias changed the title Replace recordingSpan attributes implementation [WIP] Replace recordingSpan attributes implementation Jan 26, 2022
MrAlias and others added 2 commits January 28, 2022 10:24
Do not depend on attributes being returned in a consistent order.
@codecov
Copy link

codecov bot commented Jan 28, 2022

Codecov Report

Merging #2555 (a7d714e) into main (d5292e3) will increase coverage by 0.0%.
The diff coverage is 100.0%.

Impacted file tree graph

@@          Coverage Diff          @@
##            main   #2555   +/-   ##
=====================================
  Coverage   76.0%   76.0%           
=====================================
  Files        174     173    -1     
  Lines      12190   12170   -20     
=====================================
- Hits        9268    9258   -10     
+ Misses      2677    2669    -8     
+ Partials     245     243    -2     
Impacted Files Coverage Δ
sdk/trace/tracer.go 100.0% <ø> (ø)
sdk/trace/span.go 85.3% <100.0%> (+3.2%) ⬆️
sdk/trace/batch_span_processor.go 81.1% <0.0%> (-1.0%) ⬇️
exporters/jaeger/jaeger.go 93.5% <0.0%> (+0.8%) ⬆️

@MrAlias

This comment has been minimized.

@MrAlias MrAlias marked this pull request as ready for review January 28, 2022 19:13
@MrAlias MrAlias changed the title [WIP] Replace recordingSpan attributes implementation Replace recordingSpan attributes implementation Jan 28, 2022
sdk/trace/span.go Outdated Show resolved Hide resolved
sdk/trace/span.go Show resolved Hide resolved
MrAlias and others added 2 commits January 28, 2022 14:06
Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>
trace.WithAttributes(attribute.String("key2", "value2")),
trace.WithAttributes(attribute.String("key1", "value2")),
Copy link
Contributor

Choose a reason for hiding this comment

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

Why did you change this test?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because there are two attributes set originally, the order of those attributes is a something checked indirectly. That order is not guaranteed anymore.

This updates the test to still set two attributes, similar to the original, but it updates the key-value. Resulting in a single attribute for the exported span. This update behavior is something guaranteed and is functionality also required by the specification.

Copy link
Contributor

Choose a reason for hiding this comment

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

That makes me think that our assert isn't comprehensive enough for our test cases.

But, we cover this behavior in another test, so I won't hold this up.

sdk/trace/trace_test.go Show resolved Hide resolved
Copy link
Contributor

@jmacd jmacd left a comment

Choose a reason for hiding this comment

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

This is a significant improvement, approving on the merits. For myself I'd argue to go even further.

I would optimize for StartSpan performance => keep a reference to the original []attribute.KeyValue and document that the caller MUST not modify that slice. If the slice length exceeds the maximum, call deduplicateAndDrop(). Do not check for duplicates, otherwise, just store the array.

The SetAttributes() method would be optimized to avoid deduplication in a hot path as well, if possible. As long as the total slice length will not exceed the limit, simply append(sp.attributes, newAttributes...) and defer the deduplication. If the total slice length exceeds the limit, call deduplicateAndDrop().

The call to attributesLocked() is when I would deduplicate. We don't need to allocate a map to deduplicate, we can sort in place, so the end result will be that if you only call StartSpan and never call SetAttributes, the SDK itself won't perform any allocations for span attributes.

sdk/trace/span.go Outdated Show resolved Hide resolved
sdk/trace/tracer.go Outdated Show resolved Hide resolved
@Aneurysm9
Copy link
Member

I would optimize for StartSpan performance => keep a reference to the original []attribute.KeyValue and document that the caller MUST not modify that slice.

I think that would be a significant change to the data ownership semantics and could break existing uses. I would not support making such a change without a major version bump.

Making that change in the SDK also could lead to a situation where different SDK implementations behave differently and code that is correct against one implementation could be very incorrect against another. That seems like something we should try to avoid.

Avoid the duplicate reference to the value held by the parent
TracerProvider and just refer to that directly when needed.
trace.WithAttributes(attribute.String("key2", "value2")),
trace.WithAttributes(attribute.String("key1", "value2")),
Copy link
Contributor

Choose a reason for hiding this comment

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

That makes me think that our assert isn't comprehensive enough for our test cases.

But, we cover this behavior in another test, so I won't hold this up.

@MrAlias
Copy link
Contributor Author

MrAlias commented Feb 1, 2022

I would optimize for StartSpan performance => keep a reference to the original []attribute.KeyValue and document that the caller MUST not modify that slice.

I think that would be a significant change to the data ownership semantics and could break existing uses. I would not support making such a change without a major version bump.

Making that change in the SDK also could lead to a situation where different SDK implementations behave differently and code that is correct against one implementation could be very incorrect against another. That seems like something we should try to avoid.

I agree a change to the data ownership semantics is probably not something we want to do.

However, I am intrigued at the possibility to use a slice here. If we can realize the same performance improvements shown here and potentially maintain order of the attributes according to how the user adds them it seems like something worth exploring. I put together an initial implementation of this and it looks worth developing into a full alternate approach and evaluating the merits. I will do so before merging this.

Only allocate if attributes are recorded.
@MadVikingGod
Copy link
Contributor

I just noticed that with the removal of the attributeMap and its tests we have lost coverage that (&recordingSpan).Attributes() == nil. Same that DroppedCount() == 0. As long as this is the behavior already I'm still ok with merging this, just needs an issue to go back and add tests.

@MrAlias
Copy link
Contributor Author

MrAlias commented Feb 2, 2022

I just noticed that with the removal of the attributeMap and its tests we have lost coverage that (&recordingSpan).Attributes() == nil. Same that DroppedCount() == 0. As long as this is the behavior already I'm still ok with merging this, just needs an issue to go back and add tests.

I don't see the existing test coverage, but I'm happy to add these tests.

@alolita alolita added this to the Metrics API for 1.0 milestone Feb 2, 2022
@MrAlias MrAlias changed the title Replace recordingSpan attributes implementation Replace recordingSpan attributes implementation with map of attributes Feb 3, 2022
@MrAlias MrAlias marked this pull request as draft February 3, 2022 19:56
@MrAlias
Copy link
Contributor Author

MrAlias commented Feb 3, 2022

Reverting to a draft so this doesn't accidentally get merged. I plan to present this and #2576 at the SIG meeting today and discuss which approach we should move forward with.

@MrAlias MrAlias removed this from the Metrics API for 1.0 milestone Feb 3, 2022
@MrAlias
Copy link
Contributor Author

MrAlias commented Feb 3, 2022

Closing in favor of #2576

@MrAlias MrAlias closed this Feb 3, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:trace Part of OpenTelemetry tracing bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Span attribute drop logic does not comply with the specification.
5 participants