From 164965d79e94ae119a4cbb9b57a23099a1efc473 Mon Sep 17 00:00:00 2001 From: Lev Brouk Date: Thu, 26 Mar 2026 14:12:55 -0700 Subject: [PATCH 1/2] perf(telemetry): precompute OTEL attributes for chunker hot paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace per-call `attribute.String(...)` allocation in every `Slice`/`ReadAt`/`runFetch` with precomputed `metric.MeasurementOption` values built once at package init. - `Stopwatch.end`: build one `attribute.NewSet` and reuse across all three instrument calls (histogram, sum, count) instead of three separate `metric.WithAttributes(kv...)` allocations. - New `PrecomputeAttrs(kv...)`: builds a reusable `MeasurementOption`. - New `Stopwatch.Record(ctx, total, precomputedAttrs)`: zero per-call attribute allocation alternative to `Success`/`Failure`. - Exported `Success`/`Failure` attribute vars for use with `PrecomputeAttrs`. - Add `precomputedAttrs` struct with all Slice/fetch attribute combos. - Package-level `chunkerAttrs` var built once at init. - All `timer.Success(ctx, n, attribute.String(...))` calls replaced with `timer.Record(ctx, n, a.successFromCache)` etc. FullFetchChunker.Slice on cache hit (64 MiB cache, 4K blocks, 4 MiB chunks): ``` │ baseline │ precomputed attrs │ │ sec/op │ sec/op vs base │ ChunkerSlice_CacheHit-16 20.57µ ± 2% 19.85µ ± 3% -3.49% (p=0.009 n=6) │ baseline │ precomputed attrs │ │ B/op │ B/op vs base │ ChunkerSlice_CacheHit-16 9.211Ki ± 0% 8.047Ki ± 0% -12.64% (p=0.002 n=6) │ baseline │ precomputed attrs │ │ allocs/op │ allocs/op vs base │ ChunkerSlice_CacheHit-16 16.000 ± 0% 4.000 ± 0% -75.00% (p=0.002 n=6) ``` 75% fewer allocations (16 → 4) and 13% less memory per Slice call on the NBD page fault hot path. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../orchestrator/pkg/sandbox/block/chunk.go | 103 ++++++++++++------ .../pkg/sandbox/block/chunk_bench_test.go | 60 ++++++++++ .../pkg/sandbox/block/streaming_chunk.go | 26 ++--- packages/shared/pkg/telemetry/meters.go | 29 ++++- 4 files changed, 165 insertions(+), 53 deletions(-) create mode 100644 packages/orchestrator/pkg/sandbox/block/chunk_bench_test.go diff --git a/packages/orchestrator/pkg/sandbox/block/chunk.go b/packages/orchestrator/pkg/sandbox/block/chunk.go index c3f29ea15f..527bfe0cd1 100644 --- a/packages/orchestrator/pkg/sandbox/block/chunk.go +++ b/packages/orchestrator/pkg/sandbox/block/chunk.go @@ -8,6 +8,7 @@ import ( "strconv" "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" "go.uber.org/zap" "golang.org/x/sync/errgroup" "golang.org/x/sync/singleflight" @@ -17,8 +18,67 @@ import ( "github.com/e2b-dev/infra/packages/shared/pkg/logger" "github.com/e2b-dev/infra/packages/shared/pkg/storage" "github.com/e2b-dev/infra/packages/shared/pkg/storage/header" + "github.com/e2b-dev/infra/packages/shared/pkg/telemetry" ) +const ( + pullType = "pull-type" + pullTypeLocal = "local" + pullTypeRemote = "remote" + + failureReason = "failure-reason" + + failureTypeLocalRead = "local-read" + failureTypeLocalReadAgain = "local-read-again" + failureTypeRemoteRead = "remote-read" + failureTypeCacheFetch = "cache-fetch" +) + +type precomputedAttrs struct { + successFromCache metric.MeasurementOption + successFromRemote metric.MeasurementOption + + failCacheRead metric.MeasurementOption + failRemoteFetch metric.MeasurementOption + failLocalReadAgain metric.MeasurementOption + + // RemoteReads timer (runFetch) + remoteSuccess metric.MeasurementOption + remoteFailure metric.MeasurementOption +} + +var chunkerAttrs = precomputedAttrs{ + successFromCache: telemetry.PrecomputeAttrs( + telemetry.Success, + attribute.String(pullType, pullTypeLocal)), + + successFromRemote: telemetry.PrecomputeAttrs( + telemetry.Success, + attribute.String(pullType, pullTypeRemote)), + + failCacheRead: telemetry.PrecomputeAttrs( + telemetry.Failure, + attribute.String(pullType, pullTypeLocal), + attribute.String(failureReason, failureTypeLocalRead)), + + failRemoteFetch: telemetry.PrecomputeAttrs( + telemetry.Failure, + attribute.String(pullType, pullTypeRemote), + attribute.String(failureReason, failureTypeCacheFetch)), + + failLocalReadAgain: telemetry.PrecomputeAttrs( + telemetry.Failure, + attribute.String(pullType, pullTypeLocal), + attribute.String(failureReason, failureTypeLocalReadAgain)), + + remoteSuccess: telemetry.PrecomputeAttrs( + telemetry.Success), + + remoteFailure: telemetry.PrecomputeAttrs( + telemetry.Failure, + attribute.String(failureReason, failureTypeRemoteRead)), +} + // Chunker is the interface satisfied by both FullFetchChunker and StreamingChunker. type Chunker interface { Slice(ctx context.Context, off, length int64) ([]byte, error) @@ -122,43 +182,36 @@ func (c *FullFetchChunker) WriteTo(ctx context.Context, w io.Writer) (int64, err func (c *FullFetchChunker) Slice(ctx context.Context, off, length int64) ([]byte, error) { timer := c.metrics.SlicesTimerFactory.Begin() + a := chunkerAttrs b, err := c.cache.Slice(off, length) if err == nil { - timer.Success(ctx, length, - attribute.String(pullType, pullTypeLocal)) + timer.Record(ctx, length, a.successFromCache) return b, nil } if !errors.As(err, &BytesNotAvailableError{}) { - timer.Failure(ctx, length, - attribute.String(pullType, pullTypeLocal), - attribute.String(failureReason, failureTypeLocalRead)) + timer.Record(ctx, length, a.failCacheRead) return nil, fmt.Errorf("failed read from cache at offset %d: %w", off, err) } chunkErr := c.fetchToCache(ctx, off, length) if chunkErr != nil { - timer.Failure(ctx, length, - attribute.String(pullType, pullTypeRemote), - attribute.String(failureReason, failureTypeCacheFetch)) + timer.Record(ctx, length, a.failRemoteFetch) return nil, fmt.Errorf("failed to ensure data at %d-%d: %w", off, off+length, chunkErr) } b, cacheErr := c.cache.Slice(off, length) if cacheErr != nil { - timer.Failure(ctx, length, - attribute.String(pullType, pullTypeLocal), - attribute.String(failureReason, failureTypeLocalReadAgain)) + timer.Record(ctx, length, a.failLocalReadAgain) return nil, fmt.Errorf("failed to read from cache after ensuring data at %d-%d: %w", off, off+length, cacheErr) } - timer.Success(ctx, length, - attribute.String(pullType, pullTypeRemote)) + timer.Record(ctx, length, a.successFromRemote) return b, nil } @@ -206,28 +259,25 @@ func (c *FullFetchChunker) fetchToCache(ctx context.Context, off, length int64) defer releaseCacheCloseLock() + a := chunkerAttrs fetchSW := c.metrics.RemoteReadsTimerFactory.Begin() readBytes, err := c.base.ReadAt(ctx, b, fetchOff) if err != nil { - fetchSW.Failure(ctx, int64(readBytes), - attribute.String(failureReason, failureTypeRemoteRead), - ) + fetchSW.Record(ctx, int64(readBytes), a.remoteFailure) return nil, fmt.Errorf("failed to read chunk from base %d: %w", fetchOff, err) } if readBytes != len(b) { - fetchSW.Failure(ctx, int64(readBytes), - attribute.String(failureReason, failureTypeRemoteRead), - ) + fetchSW.Record(ctx, int64(readBytes), a.remoteFailure) return nil, fmt.Errorf("failed to read chunk from base %d: expected %d bytes, got %d bytes", fetchOff, len(b), readBytes) } c.cache.setIsCached(fetchOff, int64(readBytes)) - fetchSW.Success(ctx, int64(readBytes)) + fetchSW.Record(ctx, int64(readBytes), a.remoteSuccess) return nil, nil }) @@ -251,16 +301,3 @@ func (c *FullFetchChunker) Close() error { func (c *FullFetchChunker) FileSize() (int64, error) { return c.cache.FileSize() } - -const ( - pullType = "pull-type" - pullTypeLocal = "local" - pullTypeRemote = "remote" - - failureReason = "failure-reason" - - failureTypeLocalRead = "local-read" - failureTypeLocalReadAgain = "local-read-again" - failureTypeRemoteRead = "remote-read" - failureTypeCacheFetch = "cache-fetch" -) diff --git a/packages/orchestrator/pkg/sandbox/block/chunk_bench_test.go b/packages/orchestrator/pkg/sandbox/block/chunk_bench_test.go new file mode 100644 index 0000000000..93534b4d3b --- /dev/null +++ b/packages/orchestrator/pkg/sandbox/block/chunk_bench_test.go @@ -0,0 +1,60 @@ +package block + +import ( + "context" + "path/filepath" + "testing" + + sdkmetric "go.opentelemetry.io/otel/sdk/metric" + + blockmetrics "github.com/e2b-dev/infra/packages/orchestrator/pkg/sandbox/block/metrics" +) + +const ( + cbBlockSize int64 = 4096 + cbNumBlocks int64 = 16384 // 64 MiB + cbCacheSize int64 = cbNumBlocks * cbBlockSize + cbChunkSize int64 = 4 * 1024 * 1024 // 4 MiB — MemoryChunkSize + cbChunkCount int64 = cbCacheSize / cbChunkSize +) + +// BenchmarkChunkerSlice_CacheHit benchmarks the full FullFetchChunker.Slice +// hot path on a cache hit: bitmap check + mmap slice return + OTEL +// timer.Success with attribute construction. +func BenchmarkChunkerSlice_CacheHit(b *testing.B) { + provider := sdkmetric.NewMeterProvider() + b.Cleanup(func() { provider.Shutdown(context.Background()) }) + + m, err := blockmetrics.NewMetrics(provider) + if err != nil { + b.Fatal(err) + } + + chunker, err := NewFullFetchChunker( + cbCacheSize, cbBlockSize, + nil, // base is never called on cache hit + filepath.Join(b.TempDir(), "cache"), + m, + ) + if err != nil { + b.Fatal(err) + } + b.Cleanup(func() { chunker.Close() }) + + // Pre-populate the cache so every Slice hits. + chunker.cache.setIsCached(0, cbCacheSize) + + ctx := context.Background() + + b.ResetTimer() + for i := range b.N { + off := int64(i%int(cbChunkCount)) * cbChunkSize + s, sliceErr := chunker.Slice(ctx, off, cbChunkSize) + if sliceErr != nil { + b.Fatal(sliceErr) + } + if len(s) == 0 { + b.Fatal("empty slice") + } + } +} diff --git a/packages/orchestrator/pkg/sandbox/block/streaming_chunk.go b/packages/orchestrator/pkg/sandbox/block/streaming_chunk.go index 956d71e0b3..366948da1b 100644 --- a/packages/orchestrator/pkg/sandbox/block/streaming_chunk.go +++ b/packages/orchestrator/pkg/sandbox/block/streaming_chunk.go @@ -11,7 +11,6 @@ import ( "sync/atomic" "time" - "go.opentelemetry.io/otel/attribute" "golang.org/x/sync/errgroup" "github.com/e2b-dev/infra/packages/orchestrator/pkg/sandbox/block/metrics" @@ -223,20 +222,18 @@ func (c *StreamingChunker) WriteTo(ctx context.Context, w io.Writer) (int64, err func (c *StreamingChunker) Slice(ctx context.Context, off, length int64) ([]byte, error) { timer := c.metrics.SlicesTimerFactory.Begin() + a := chunkerAttrs // Fast path: already cached b, err := c.cache.Slice(off, length) if err == nil { - timer.Success(ctx, length, - attribute.String(pullType, pullTypeLocal)) + timer.Record(ctx, length, a.successFromCache) return b, nil } if !errors.As(err, &BytesNotAvailableError{}) { - timer.Failure(ctx, length, - attribute.String(pullType, pullTypeLocal), - attribute.String(failureReason, failureTypeLocalRead)) + timer.Record(ctx, length, a.failCacheRead) return nil, fmt.Errorf("failed read from cache at offset %d: %w", off, err) } @@ -269,24 +266,19 @@ func (c *StreamingChunker) Slice(ctx context.Context, off, length int64) ([]byte } if err := eg.Wait(); err != nil { - timer.Failure(ctx, length, - attribute.String(pullType, pullTypeRemote), - attribute.String(failureReason, failureTypeCacheFetch)) + timer.Record(ctx, length, a.failRemoteFetch) return nil, fmt.Errorf("failed to ensure data at %d-%d: %w", off, off+length, err) } b, cacheErr := c.cache.Slice(off, length) if cacheErr != nil { - timer.Failure(ctx, length, - attribute.String(pullType, pullTypeLocal), - attribute.String(failureReason, failureTypeLocalReadAgain)) + timer.Record(ctx, length, a.failLocalReadAgain) return nil, fmt.Errorf("failed to read from cache after ensuring data at %d-%d: %w", off, off+length, cacheErr) } - timer.Success(ctx, length, - attribute.String(pullType, pullTypeRemote)) + timer.Record(ctx, length, a.successFromRemote) return b, nil } @@ -382,19 +374,19 @@ func (c *StreamingChunker) runFetch(ctx context.Context, s *fetchSession) { } defer releaseLock() + a := chunkerAttrs fetchTimer := c.metrics.RemoteReadsTimerFactory.Begin() err = c.progressiveRead(ctx, s, mmapSlice) if err != nil { - fetchTimer.Failure(ctx, s.chunkLen, - attribute.String(failureReason, failureTypeRemoteRead)) + fetchTimer.Record(ctx, s.chunkLen, a.remoteFailure) s.setError(err, false) return } - fetchTimer.Success(ctx, s.chunkLen) + fetchTimer.Record(ctx, s.chunkLen, a.remoteSuccess) s.setDone() } diff --git a/packages/shared/pkg/telemetry/meters.go b/packages/shared/pkg/telemetry/meters.go index 70ad5da013..bb4497ce7a 100644 --- a/packages/shared/pkg/telemetry/meters.go +++ b/packages/shared/pkg/telemetry/meters.go @@ -411,6 +411,12 @@ const ( resultTypeFailure = "failure" ) +var ( + // Pre-allocated result attributes for use with PrecomputeAttrs. + Success = attribute.String(resultAttr, resultTypeSuccess) + Failure = attribute.String(resultAttr, resultTypeFailure) +) + func (t Stopwatch) Success(ctx context.Context, total int64, kv ...attribute.KeyValue) { t.end(ctx, resultTypeSuccess, total, kv...) } @@ -424,7 +430,24 @@ func (t Stopwatch) end(ctx context.Context, result string, total int64, kv ...at kv = append(t.kv, kv...) amount := time.Since(t.start).Milliseconds() - t.histogram.Record(ctx, amount, metric.WithAttributes(kv...)) - t.sum.Add(ctx, total, metric.WithAttributes(kv...)) - t.count.Add(ctx, 1, metric.WithAttributes(kv...)) + opt := metric.WithAttributeSet(attribute.NewSet(kv...)) + t.histogram.Record(ctx, amount, opt) + t.sum.Add(ctx, total, opt) + t.count.Add(ctx, 1, opt) +} + +// PrecomputeAttrs builds a reusable MeasurementOption from the given attribute +// key-values. The option must include all attributes (including "result"). +// Use with Stopwatch.Record to avoid per-call attribute allocation. +func PrecomputeAttrs(kv ...attribute.KeyValue) metric.MeasurementOption { + return metric.WithAttributeSet(attribute.NewSet(kv...)) +} + +// Record records an operation using a precomputed attribute option. +// Zero-allocation alternative to Success/Failure for hot paths. +func (t Stopwatch) Record(ctx context.Context, total int64, precomputedAttrs metric.MeasurementOption) { + amount := time.Since(t.start).Milliseconds() + t.histogram.Record(ctx, amount, precomputedAttrs) + t.sum.Add(ctx, total, precomputedAttrs) + t.count.Add(ctx, 1, precomputedAttrs) } From ad6161d6214d9ce140c0b650d56c756d27f288f1 Mon Sep 17 00:00:00 2001 From: Lev Brouk Date: Fri, 27 Mar 2026 05:16:54 -0700 Subject: [PATCH 2/2] PR feedback: - renamed `Record` -> `RecordRaw` - dropped the `a` alias - refactored `end()` to call `RecordRaw` --- .../orchestrator/pkg/sandbox/block/chunk.go | 18 ++++++++---------- .../pkg/sandbox/block/streaming_chunk.go | 16 +++++++--------- packages/shared/pkg/telemetry/meters.go | 13 +++++-------- 3 files changed, 20 insertions(+), 27 deletions(-) diff --git a/packages/orchestrator/pkg/sandbox/block/chunk.go b/packages/orchestrator/pkg/sandbox/block/chunk.go index 527bfe0cd1..ad2017d2aa 100644 --- a/packages/orchestrator/pkg/sandbox/block/chunk.go +++ b/packages/orchestrator/pkg/sandbox/block/chunk.go @@ -182,36 +182,35 @@ func (c *FullFetchChunker) WriteTo(ctx context.Context, w io.Writer) (int64, err func (c *FullFetchChunker) Slice(ctx context.Context, off, length int64) ([]byte, error) { timer := c.metrics.SlicesTimerFactory.Begin() - a := chunkerAttrs b, err := c.cache.Slice(off, length) if err == nil { - timer.Record(ctx, length, a.successFromCache) + timer.RecordRaw(ctx, length, chunkerAttrs.successFromCache) return b, nil } if !errors.As(err, &BytesNotAvailableError{}) { - timer.Record(ctx, length, a.failCacheRead) + timer.RecordRaw(ctx, length, chunkerAttrs.failCacheRead) return nil, fmt.Errorf("failed read from cache at offset %d: %w", off, err) } chunkErr := c.fetchToCache(ctx, off, length) if chunkErr != nil { - timer.Record(ctx, length, a.failRemoteFetch) + timer.RecordRaw(ctx, length, chunkerAttrs.failRemoteFetch) return nil, fmt.Errorf("failed to ensure data at %d-%d: %w", off, off+length, chunkErr) } b, cacheErr := c.cache.Slice(off, length) if cacheErr != nil { - timer.Record(ctx, length, a.failLocalReadAgain) + timer.RecordRaw(ctx, length, chunkerAttrs.failLocalReadAgain) return nil, fmt.Errorf("failed to read from cache after ensuring data at %d-%d: %w", off, off+length, cacheErr) } - timer.Record(ctx, length, a.successFromRemote) + timer.RecordRaw(ctx, length, chunkerAttrs.successFromRemote) return b, nil } @@ -259,25 +258,24 @@ func (c *FullFetchChunker) fetchToCache(ctx context.Context, off, length int64) defer releaseCacheCloseLock() - a := chunkerAttrs fetchSW := c.metrics.RemoteReadsTimerFactory.Begin() readBytes, err := c.base.ReadAt(ctx, b, fetchOff) if err != nil { - fetchSW.Record(ctx, int64(readBytes), a.remoteFailure) + fetchSW.RecordRaw(ctx, int64(readBytes), chunkerAttrs.remoteFailure) return nil, fmt.Errorf("failed to read chunk from base %d: %w", fetchOff, err) } if readBytes != len(b) { - fetchSW.Record(ctx, int64(readBytes), a.remoteFailure) + fetchSW.RecordRaw(ctx, int64(readBytes), chunkerAttrs.remoteFailure) return nil, fmt.Errorf("failed to read chunk from base %d: expected %d bytes, got %d bytes", fetchOff, len(b), readBytes) } c.cache.setIsCached(fetchOff, int64(readBytes)) - fetchSW.Record(ctx, int64(readBytes), a.remoteSuccess) + fetchSW.RecordRaw(ctx, int64(readBytes), chunkerAttrs.remoteSuccess) return nil, nil }) diff --git a/packages/orchestrator/pkg/sandbox/block/streaming_chunk.go b/packages/orchestrator/pkg/sandbox/block/streaming_chunk.go index 366948da1b..7e40b35c4e 100644 --- a/packages/orchestrator/pkg/sandbox/block/streaming_chunk.go +++ b/packages/orchestrator/pkg/sandbox/block/streaming_chunk.go @@ -222,18 +222,17 @@ func (c *StreamingChunker) WriteTo(ctx context.Context, w io.Writer) (int64, err func (c *StreamingChunker) Slice(ctx context.Context, off, length int64) ([]byte, error) { timer := c.metrics.SlicesTimerFactory.Begin() - a := chunkerAttrs // Fast path: already cached b, err := c.cache.Slice(off, length) if err == nil { - timer.Record(ctx, length, a.successFromCache) + timer.RecordRaw(ctx, length, chunkerAttrs.successFromCache) return b, nil } if !errors.As(err, &BytesNotAvailableError{}) { - timer.Record(ctx, length, a.failCacheRead) + timer.RecordRaw(ctx, length, chunkerAttrs.failCacheRead) return nil, fmt.Errorf("failed read from cache at offset %d: %w", off, err) } @@ -266,19 +265,19 @@ func (c *StreamingChunker) Slice(ctx context.Context, off, length int64) ([]byte } if err := eg.Wait(); err != nil { - timer.Record(ctx, length, a.failRemoteFetch) + timer.RecordRaw(ctx, length, chunkerAttrs.failRemoteFetch) return nil, fmt.Errorf("failed to ensure data at %d-%d: %w", off, off+length, err) } b, cacheErr := c.cache.Slice(off, length) if cacheErr != nil { - timer.Record(ctx, length, a.failLocalReadAgain) + timer.RecordRaw(ctx, length, chunkerAttrs.failLocalReadAgain) return nil, fmt.Errorf("failed to read from cache after ensuring data at %d-%d: %w", off, off+length, cacheErr) } - timer.Record(ctx, length, a.successFromRemote) + timer.RecordRaw(ctx, length, chunkerAttrs.successFromRemote) return b, nil } @@ -374,19 +373,18 @@ func (c *StreamingChunker) runFetch(ctx context.Context, s *fetchSession) { } defer releaseLock() - a := chunkerAttrs fetchTimer := c.metrics.RemoteReadsTimerFactory.Begin() err = c.progressiveRead(ctx, s, mmapSlice) if err != nil { - fetchTimer.Record(ctx, s.chunkLen, a.remoteFailure) + fetchTimer.RecordRaw(ctx, s.chunkLen, chunkerAttrs.remoteFailure) s.setError(err, false) return } - fetchTimer.Record(ctx, s.chunkLen, a.remoteSuccess) + fetchTimer.RecordRaw(ctx, s.chunkLen, chunkerAttrs.remoteSuccess) s.setDone() } diff --git a/packages/shared/pkg/telemetry/meters.go b/packages/shared/pkg/telemetry/meters.go index bb4497ce7a..b6174169c4 100644 --- a/packages/shared/pkg/telemetry/meters.go +++ b/packages/shared/pkg/telemetry/meters.go @@ -428,12 +428,8 @@ func (t Stopwatch) Failure(ctx context.Context, total int64, kv ...attribute.Key func (t Stopwatch) end(ctx context.Context, result string, total int64, kv ...attribute.KeyValue) { kv = append(kv, attribute.KeyValue{Key: resultAttr, Value: attribute.StringValue(result)}) kv = append(t.kv, kv...) - - amount := time.Since(t.start).Milliseconds() opt := metric.WithAttributeSet(attribute.NewSet(kv...)) - t.histogram.Record(ctx, amount, opt) - t.sum.Add(ctx, total, opt) - t.count.Add(ctx, 1, opt) + t.RecordRaw(ctx, total, opt) } // PrecomputeAttrs builds a reusable MeasurementOption from the given attribute @@ -443,9 +439,10 @@ func PrecomputeAttrs(kv ...attribute.KeyValue) metric.MeasurementOption { return metric.WithAttributeSet(attribute.NewSet(kv...)) } -// Record records an operation using a precomputed attribute option. -// Zero-allocation alternative to Success/Failure for hot paths. -func (t Stopwatch) Record(ctx context.Context, total int64, precomputedAttrs metric.MeasurementOption) { +// RecordRaw records an operation using a precomputed attribute option, it does +// not include any previous attributes passed at Begin(). Zero-allocation +// alternative to Success/Failure for hot paths. +func (t Stopwatch) RecordRaw(ctx context.Context, total int64, precomputedAttrs metric.MeasurementOption) { amount := time.Since(t.start).Milliseconds() t.histogram.Record(ctx, amount, precomputedAttrs) t.sum.Add(ctx, total, precomputedAttrs)