The span export pipeline has three unnecessary clone/allocation points that compound on every export cycle. The log pipeline already avoids most of these via the borrowed LogBatch pattern, but spans still take the expensive ownership path.
1. batch.split_off(0) allocates a new Vec every export cycle
In opentelemetry-sdk/src/trace/span_processor.rs, export_batch_sync calls batch.split_off(0) to pass owned data to the exporter. This allocates a fresh Vec on every export. There's already a TODO comment acknowledging this:
// TODO: Compared to Logs, this requires new allocation for vec for
// every export. See if this can be optimized by
// *not* requiring ownership in the exporter.
The log side avoids this entirely because LogExporter::export() takes a borrowed LogBatch<'_>.
2. Deep clone of the entire batch per retry attempt
In opentelemetry-otlp/src/exporter/tonic/trace.rs, the export wraps the batch in Arc<Vec<SpanData>> and then on each retry iteration does:
group_spans_by_resource_and_scope((*batch_clone).clone(), &self.resource)
That (*batch_clone).clone() deep-clones every SpanData in the batch on every attempt, including the first one. If the data were serialized to bytes once before retrying, retries would just resend the same bytes.
3. Each SpanData is cloned during protobuf conversion
In opentelemetry-proto/src/transform/trace.rs, group_spans_by_resource_and_scope iterates over &SpanData references but then clones each one to convert it:
.map(|span_data| span_data.clone().into())
The From<SpanData> for Span impl already consumes the SpanData by value, so this clone is forced by the function signature taking Vec<SpanData> while also needing references for grouping. Taking ownership and draining the vec would avoid this.
Suggested approach
- Change
SpanExporter::export() to accept borrowed data (like LogExporter does with LogBatch), removing the need for split_off(0) in the batch processor. This is the biggest API change and would fix point 1.
- Make
group_spans_by_resource_and_scope consume the Vec instead of borrowing it, so it can move SpanData into proto Spans without cloning. Fixes point 3.
- Serialize the protobuf request once before the retry loop, so retries resend bytes instead of re-converting from SpanData. Fixes point 2.
Related
The span export pipeline has three unnecessary clone/allocation points that compound on every export cycle. The log pipeline already avoids most of these via the borrowed
LogBatchpattern, but spans still take the expensive ownership path.1.
batch.split_off(0)allocates a new Vec every export cycleIn
opentelemetry-sdk/src/trace/span_processor.rs,export_batch_synccallsbatch.split_off(0)to pass owned data to the exporter. This allocates a fresh Vec on every export. There's already a TODO comment acknowledging this:The log side avoids this entirely because
LogExporter::export()takes a borrowedLogBatch<'_>.2. Deep clone of the entire batch per retry attempt
In
opentelemetry-otlp/src/exporter/tonic/trace.rs, the export wraps the batch inArc<Vec<SpanData>>and then on each retry iteration does:That
(*batch_clone).clone()deep-clones everySpanDatain the batch on every attempt, including the first one. If the data were serialized to bytes once before retrying, retries would just resend the same bytes.3. Each
SpanDatais cloned during protobuf conversionIn
opentelemetry-proto/src/transform/trace.rs,group_spans_by_resource_and_scopeiterates over&SpanDatareferences but then clones each one to convert it:The
From<SpanData> for Spanimpl already consumes the SpanData by value, so this clone is forced by the function signature takingVec<SpanData>while also needing references for grouping. Taking ownership and draining the vec would avoid this.Suggested approach
SpanExporter::export()to accept borrowed data (likeLogExporterdoes withLogBatch), removing the need forsplit_off(0)in the batch processor. This is the biggest API change and would fix point 1.group_spans_by_resource_and_scopeconsume the Vec instead of borrowing it, so it can move SpanData into proto Spans without cloning. Fixes point 3.Related
&selfcleanup (related but orthogonal; addresses mutability, not ownership of batch data)