1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-11-27 22:49:15 +02:00

trace: Use non-generic to replace newEvictedQueue in trace.start to reduce memory usage. (#5497)

benchstat:
```
goos: darwin
goarch: arm64
pkg: go.opentelemetry.io/otel/sdk/trace
              │     old     │                 new                 │
              │   sec/op    │   sec/op     vs base                │
TraceStart-10   950.6n ± 1%   641.0n ± 0%  -32.57% (p=0.000 n=10)

              │     old     │                new                 │
              │    B/op     │    B/op     vs base                │
TraceStart-10   1040.0 ± 0%   704.0 ± 0%  -32.31% (p=0.000 n=10)

              │    old     │                new                 │
              │ allocs/op  │ allocs/op   vs base                │
TraceStart-10   20.00 ± 0%   14.00 ± 0%  -30.00% (p=0.000 n=10)
```

---------

Co-authored-by: Damien Mathieu <damien.mathieu@elastic.co>
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
This commit is contained in:
ttoad
2024-06-17 22:39:03 +08:00
committed by GitHub
parent d3f3ae1036
commit 30e82e01b6
5 changed files with 43 additions and 24 deletions

View File

@@ -27,6 +27,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- The exporter no longer exports the deprecated "otel.library.name" or "otel.library.version" attributes. - The exporter no longer exports the deprecated "otel.library.name" or "otel.library.version" attributes.
- Upgrade `go.opentelemetry.io/otel/semconv/v1.25.0` to `go.opentelemetry.io/otel/semconv/v1.26.0` in `go.opentelemetry.io/otel/sdk/resource`. (#5490) - Upgrade `go.opentelemetry.io/otel/semconv/v1.25.0` to `go.opentelemetry.io/otel/semconv/v1.26.0` in `go.opentelemetry.io/otel/sdk/resource`. (#5490)
- Upgrade `go.opentelemetry.io/otel/semconv/v1.25.0` to `go.opentelemetry.io/otel/semconv/v1.26.0` in `go.opentelemetry.io/otel/sdk/trace`. (#5490) - Upgrade `go.opentelemetry.io/otel/semconv/v1.25.0` to `go.opentelemetry.io/otel/semconv/v1.26.0` in `go.opentelemetry.io/otel/sdk/trace`. (#5490)
- Use non-generic functions in the `Start` method of `"go.opentelemetry.io/otel/sdk/trace".Trace` to reduce memory allocation. (#5497)
### Fixed ### Fixed

View File

@@ -4,7 +4,6 @@
package trace // import "go.opentelemetry.io/otel/sdk/trace" package trace // import "go.opentelemetry.io/otel/sdk/trace"
import ( import (
"fmt"
"slices" "slices"
"sync" "sync"
@@ -19,13 +18,19 @@ type evictedQueue[T any] struct {
logDropped func() logDropped func()
} }
func newEvictedQueue[T any](capacity int) evictedQueue[T] { func newEvictedQueueEvent(capacity int) evictedQueue[Event] {
var tVal T
msg := fmt.Sprintf("limit reached: dropping trace %T", tVal)
// Do not pre-allocate queue, do this lazily. // Do not pre-allocate queue, do this lazily.
return evictedQueue[T]{ return evictedQueue[Event]{
capacity: capacity, capacity: capacity,
logDropped: sync.OnceFunc(func() { global.Warn(msg) }), logDropped: sync.OnceFunc(func() { global.Warn("limit reached: dropping trace trace.Event") }),
}
}
func newEvictedQueueLink(capacity int) evictedQueue[Link] {
// Do not pre-allocate queue, do this lazily.
return evictedQueue[Link]{
capacity: capacity,
logDropped: sync.OnceFunc(func() { global.Warn("limit reached: dropping trace trace.Link") }),
} }
} }

View File

@@ -14,47 +14,47 @@ func init() {
} }
func TestAdd(t *testing.T) { func TestAdd(t *testing.T) {
q := newEvictedQueue[string](3) q := newEvictedQueueLink(3)
q.add("value1") q.add(Link{})
q.add("value2") q.add(Link{})
if wantLen, gotLen := 2, len(q.queue); wantLen != gotLen { if wantLen, gotLen := 2, len(q.queue); wantLen != gotLen {
t.Errorf("got queue length %d want %d", gotLen, wantLen) t.Errorf("got queue length %d want %d", gotLen, wantLen)
} }
} }
func TestCopy(t *testing.T) { func TestCopy(t *testing.T) {
q := newEvictedQueue[string](3) q := newEvictedQueueEvent(3)
q.add("value1") q.add(Event{Name: "value1"})
cp := q.copy() cp := q.copy()
q.add("value2") q.add(Event{Name: "value2"})
assert.Equal(t, []string{"value1"}, cp, "queue update modified copy") assert.Equal(t, []Event{{Name: "value1"}}, cp, "queue update modified copy")
cp[0] = "value0" cp[0] = Event{Name: "value0"}
assert.Equal(t, "value1", q.queue[0], "copy update modified queue") assert.Equal(t, Event{Name: "value1"}, q.queue[0], "copy update modified queue")
} }
func TestDropCount(t *testing.T) { func TestDropCount(t *testing.T) {
q := newEvictedQueue[string](3) q := newEvictedQueueEvent(3)
var called bool var called bool
q.logDropped = func() { called = true } q.logDropped = func() { called = true }
q.add("value1") q.add(Event{Name: "value1"})
assert.False(t, called, `"value1" logged as dropped`) assert.False(t, called, `"value1" logged as dropped`)
q.add("value2") q.add(Event{Name: "value2"})
assert.False(t, called, `"value2" logged as dropped`) assert.False(t, called, `"value2" logged as dropped`)
q.add("value3") q.add(Event{Name: "value3"})
assert.False(t, called, `"value3" logged as dropped`) assert.False(t, called, `"value3" logged as dropped`)
q.add("value1") q.add(Event{Name: "value1"})
assert.True(t, called, `"value2" not logged as dropped`) assert.True(t, called, `"value2" not logged as dropped`)
q.add("value4") q.add(Event{Name: "value4"})
if wantLen, gotLen := 3, len(q.queue); wantLen != gotLen { if wantLen, gotLen := 3, len(q.queue); wantLen != gotLen {
t.Errorf("got queue length %d want %d", gotLen, wantLen) t.Errorf("got queue length %d want %d", gotLen, wantLen)
} }
if wantDropCount, gotDropCount := 2, q.droppedCount; wantDropCount != gotDropCount { if wantDropCount, gotDropCount := 2, q.droppedCount; wantDropCount != gotDropCount {
t.Errorf("got drop count %d want %d", gotDropCount, wantDropCount) t.Errorf("got drop count %d want %d", gotDropCount, wantDropCount)
} }
wantArr := []string{"value3", "value1", "value4"} wantArr := []Event{{Name: "value3"}, {Name: "value1"}, {Name: "value4"}}
gotArr := q.copy() gotArr := q.copy()
if wantLen, gotLen := len(wantArr), len(gotArr); gotLen != wantLen { if wantLen, gotLen := len(wantArr), len(gotArr); gotLen != wantLen {

View File

@@ -2107,3 +2107,16 @@ func TestAddLinkToNonRecordingSpan(t *testing.T) {
t.Errorf("AddLinkToNonRecordingSpan: -got +want %s", diff) t.Errorf("AddLinkToNonRecordingSpan: -got +want %s", diff)
} }
} }
func BenchmarkTraceStart(b *testing.B) {
tracer := NewTracerProvider().Tracer("")
ctx := trace.ContextWithSpanContext(context.Background(), trace.SpanContext{})
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, span := tracer.Start(ctx, "")
span.End()
}
}

View File

@@ -132,8 +132,8 @@ func (tr *tracer) newRecordingSpan(psc, sc trace.SpanContext, name string, sr Sa
spanKind: trace.ValidateSpanKind(config.SpanKind()), spanKind: trace.ValidateSpanKind(config.SpanKind()),
name: name, name: name,
startTime: startTime, startTime: startTime,
events: newEvictedQueue[Event](tr.provider.spanLimits.EventCountLimit), events: newEvictedQueueEvent(tr.provider.spanLimits.EventCountLimit),
links: newEvictedQueue[Link](tr.provider.spanLimits.LinkCountLimit), links: newEvictedQueueLink(tr.provider.spanLimits.LinkCountLimit),
tracer: tr, tracer: tr,
} }