Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
27c18fd
Add tracer benchmark and record baseline
dbarker Jul 15, 2026
9746077
Performance optimizations for SDK Tracer::StartSpan
dbarker Jul 15, 2026
e472646
fix iwyu warnings and cleanup
dbarker Jul 15, 2026
2192679
fix clang-tidy warnings and clean up
dbarker Jul 15, 2026
f3f6d01
fix noexcept build
dbarker Jul 15, 2026
84cab3c
add missing file
dbarker Jul 15, 2026
9caf280
fix opentracing shim test after default value of the parent context i…
dbarker Jul 15, 2026
b0140b2
The DefaultSpan is a non-recording span, use it in tracer.cc and dele…
dbarker Jul 15, 2026
38a0f60
Set the in memory span exporter buffer to 0 for the tracer benchmark …
dbarker Jul 15, 2026
4bde64c
add changelog entry
dbarker Jul 15, 2026
f0e6367
Merge branch 'main' into perf_start_span_optimizations
dbarker Jul 16, 2026
a815cbd
Merge branch 'main' into perf_start_span_optimizations
marcalff Jul 16, 2026
4ae94f0
Merge branch 'main' into perf_start_span_optimizations
dbarker Jul 17, 2026
067c2b3
use the api GetSpanContext in StartSpan. Get only one ContextValue pe…
dbarker Jul 17, 2026
ddb176b
update results for the tracer and sampler benchmarks after merging in…
dbarker Jul 17, 2026
2852840
fix iwyu warning
dbarker Jul 17, 2026
3c6e1d0
add sampler benchmark to cover samplers that add attributes to the span
dbarker Jul 18, 2026
2e42634
pass sampling result to the SDK Span constructor in order to add attr…
dbarker Jul 18, 2026
251128e
Merge branch 'main' into perf_start_span_optimizations
dbarker Jul 18, 2026
02d6394
fix iwyu warnings
dbarker Jul 18, 2026
b208179
fix bazel.noexcept build
dbarker Jul 18, 2026
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ Breaking changes:

## [1.28.0] 2026-07-16

* [SDK] Add Tracer::StartSpan benchmark and optimizations
[#4248](https://github.com/open-telemetry/opentelemetry-cpp/pull/4248)

* [RELEASE] Bump main branch to 1.28.0-dev
[#4081](https://github.com/open-telemetry/opentelemetry-cpp/pull/4081)

Expand Down
7 changes: 4 additions & 3 deletions api/include/opentelemetry/trace/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,14 @@ inline nostd::shared_ptr<Span> GetSpan(const context::Context &context) noexcept
// stored directly in the context, and returns an invalid SpanContext if neither is present.
inline SpanContext GetSpanContext(const context::Context &context) noexcept
{
if (!context.HasKey(kSpanKey))
const context::ContextValue context_value = context.GetValue(kSpanKey);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

removed context.HasKey call since it just calls context.GetValue(key) and checks for the nostd::monostate. The span key lookup and context value copy can be done once.


if (nostd::holds_alternative<nostd::monostate>(context_value))
{
// The context does not have a span or span context
return SpanContext::GetInvalid();
}

const context::ContextValue context_value = context.GetValue(kSpanKey);

// Get the span metadata from the active span in the context.
if (const nostd::shared_ptr<Span> *maybe_span =
nostd::get_if<nostd::shared_ptr<Span>>(&context_value))
Expand Down
3 changes: 2 additions & 1 deletion api/include/opentelemetry/trace/default_span.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace trace
{

/**
* DefaultSpan provides a non-operational Span that propagates
* DefaultSpan provides a non-recording Span that propagates
* the tracer context by wrapping it inside the Span object.
*/

Expand All @@ -28,6 +28,7 @@ class DefaultSpan : public Span

trace::SpanContext GetContext() const noexcept override { return span_context_; }

// Returns false, as DefaultSpan is a non-recording span.
bool IsRecording() const noexcept override { return false; }

void SetAttribute(nostd::string_view /* key */,
Expand Down
2 changes: 1 addition & 1 deletion api/include/opentelemetry/trace/span_startoptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ struct StartSpanOptions
// - If the `parent` field is not set, the newly created Span will inherit the
// parent of the currently active Span (if any) in the current context.
//
nostd::variant<SpanContext, context::Context> parent = SpanContext::GetInvalid();
nostd::variant<SpanContext, context::Context> parent = context::Context{};

@dbarker dbarker Jul 17, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

For most callers the value of parent is never changed. For callers that set a parent the initial value is often default constructed then replaced. To keep this change ABI safe we don't add a nostd::monostate (which is really what we want) and instead construct the cheapest object by default. The SpanContext is a complex object with a shared_ptr<TraceState> that is initialized to a global TraceState default object. The full Context is simpler and only has a shared_ptr<DataList> member that is default initialized to null.


// TODO:
// SpanContext remote_parent;
Expand Down
12 changes: 6 additions & 6 deletions opentracing-shim/test/shim_utils_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "opentelemetry/common/attribute_value.h"
#include "opentelemetry/common/key_value_iterable.h"
#include "opentelemetry/common/timestamp.h"
#include "opentelemetry/context/context.h"
#include "opentelemetry/context/runtime_context.h"
#include "opentelemetry/nostd/function_ref.h"
#include "opentelemetry/nostd/shared_ptr.h"
Expand All @@ -34,13 +35,13 @@
#include "opentelemetry/opentracingshim/shim_utils.h"
#include "opentelemetry/opentracingshim/span_context_shim.h"
#include "opentelemetry/trace/span_context.h"
#include "opentelemetry/trace/span_metadata.h"
#include "opentelemetry/trace/span_startoptions.h"

#include "shim_mocks.h"

namespace trace_api = opentelemetry::trace;
namespace baggage = opentelemetry::baggage;
namespace common = opentelemetry::common;
namespace context = opentelemetry::context;
namespace nostd = opentelemetry::nostd;
namespace shim = opentelemetry::opentracingshim;

Expand Down Expand Up @@ -138,8 +139,7 @@ TEST(ShimUtilsTest, MakeOptionsShim_EmptyRefs)
common::SystemTimestamp{options.start_system_timestamp});
ASSERT_EQ(options_shim.start_steady_time,
common::SteadyTimestamp{options.start_steady_timestamp});
ASSERT_EQ(nostd::get<trace_api::SpanContext>(options_shim.parent),
trace_api::SpanContext::GetInvalid());
ASSERT_FALSE(nostd::get<context::Context>(options_shim.parent).HasKey(trace_api::kSpanKey));
}

TEST(ShimUtilsTest, MakeOptionsShim_InvalidSpanContext)
Expand All @@ -154,8 +154,8 @@ TEST(ShimUtilsTest, MakeOptionsShim_InvalidSpanContext)
common::SystemTimestamp{options.start_system_timestamp});
ASSERT_EQ(options_shim.start_steady_time,
common::SteadyTimestamp{options.start_steady_timestamp});
ASSERT_EQ(nostd::get<trace_api::SpanContext>(options_shim.parent),
trace_api::SpanContext::GetInvalid());

ASSERT_FALSE(nostd::get<context::Context>(options_shim.parent).HasKey(trace_api::kSpanKey));
}

TEST(ShimUtilsTest, MakeOptionsShim_FirstChildOf)
Expand Down
4 changes: 2 additions & 2 deletions sdk/include/opentelemetry/sdk/trace/sampler.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ struct SamplingResult
// The tracestate used by the span.
nostd::shared_ptr<opentelemetry::trace::TraceState> trace_state;

inline bool IsRecording()
inline bool IsRecording() const noexcept
{
return decision == Decision::RECORD_ONLY || decision == Decision::RECORD_AND_SAMPLE;
}
inline bool IsSampled() { return decision == Decision::RECORD_AND_SAMPLE; }
inline bool IsSampled() const noexcept { return decision == Decision::RECORD_AND_SAMPLE; }
};

/**
Expand Down
1 change: 1 addition & 0 deletions sdk/include/opentelemetry/sdk/trace/tracer.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ class Tracer final : public opentelemetry::trace::Tracer,
#if OPENTELEMETRY_ABI_VERSION_NO < 2
std::atomic<bool> is_enabled_{false};
#endif
nostd::shared_ptr<opentelemetry::trace::Span> noop_span_;
};
} // namespace trace
} // namespace sdk
Expand Down
20 changes: 15 additions & 5 deletions sdk/src/trace/span.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

#include <chrono>
#include <map>
#include <utility>

#include "opentelemetry/nostd/function_ref.h"
Expand Down Expand Up @@ -55,8 +56,9 @@ Span::Span(std::shared_ptr<Tracer> &&tracer,
const common::KeyValueIterable &attributes,
const opentelemetry::trace::SpanContextKeyValueIterable &links,
const opentelemetry::trace::StartSpanOptions &options,
const opentelemetry::sdk::trace::SamplingResult &sampling_result,
const opentelemetry::trace::SpanContext &parent_span_context,
std::unique_ptr<opentelemetry::trace::SpanContext> span_context) noexcept
opentelemetry::trace::SpanContext span_context) noexcept
: tracer_{std::move(tracer)},
recordable_{tracer_->GetProcessor().MakeRecordable()},
start_steady_time{options.start_steady_time},
Expand All @@ -69,11 +71,11 @@ Span::Span(std::shared_ptr<Tracer> &&tracer,
recordable_->SetSpanLimits(tracer_->GetSpanLimits());
recordable_->SetName(name);
recordable_->SetInstrumentationScope(tracer_->GetInstrumentationScope());
recordable_->SetIdentity(*span_context_, parent_span_context.IsValid()
? parent_span_context.span_id()
: opentelemetry::trace::SpanId());
recordable_->SetIdentity(span_context_, parent_span_context.IsValid()
? parent_span_context.span_id()
: opentelemetry::trace::SpanId());

recordable_->SetTraceFlags(span_context_->trace_flags());
recordable_->SetTraceFlags(span_context_.trace_flags());

attributes.ForEachKeyValue([&](nostd::string_view key, common::AttributeValue value) noexcept {
recordable_->SetAttribute(key, value);
Expand All @@ -86,6 +88,14 @@ Span::Span(std::shared_ptr<Tracer> &&tracer,
return true;
});

if (sampling_result.attributes != nullptr)
{
for (const auto &kv : *sampling_result.attributes)
{
recordable_->SetAttribute(kv.first, kv.second);
}
}

recordable_->SetSpanKind(options.kind);
recordable_->SetStartTime(NowOr(options.start_system_time));
start_steady_time = NowOr(options.start_steady_time);
Expand Down
11 changes: 5 additions & 6 deletions sdk/src/trace/span.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "opentelemetry/common/timestamp.h"
#include "opentelemetry/nostd/string_view.h"
#include "opentelemetry/sdk/trace/recordable.h"
#include "opentelemetry/sdk/trace/sampler.h"
#include "opentelemetry/sdk/trace/tracer.h"
#include "opentelemetry/trace/span.h"
#include "opentelemetry/trace/span_context.h"
Expand All @@ -31,8 +32,9 @@ class Span final : public opentelemetry::trace::Span
const opentelemetry::common::KeyValueIterable &attributes,
const opentelemetry::trace::SpanContextKeyValueIterable &links,
const opentelemetry::trace::StartSpanOptions &options,
const opentelemetry::sdk::trace::SamplingResult &sampling_result,
const opentelemetry::trace::SpanContext &parent_span_context,
std::unique_ptr<opentelemetry::trace::SpanContext> span_context) noexcept;
opentelemetry::trace::SpanContext span_context) noexcept;

Span(const Span &) = delete;
Span(Span &&) = delete;
Expand Down Expand Up @@ -73,17 +75,14 @@ class Span final : public opentelemetry::trace::Span

bool IsRecording() const noexcept override;

opentelemetry::trace::SpanContext GetContext() const noexcept override
{
return *span_context_.get();
}
opentelemetry::trace::SpanContext GetContext() const noexcept override { return span_context_; }

private:
std::shared_ptr<Tracer> tracer_;
mutable std::mutex mu_;
std::unique_ptr<Recordable> recordable_;
opentelemetry::common::SteadyTimestamp start_steady_time;
std::unique_ptr<opentelemetry::trace::SpanContext> span_context_;
opentelemetry::trace::SpanContext span_context_;
bool has_ended_{false};
};
} // namespace trace
Expand Down
Loading
Loading