diff --git a/pkg/obs/ash/BUILD.bazel b/pkg/obs/ash/BUILD.bazel index e97e54620a70..61e73c76fefb 100644 --- a/pkg/obs/ash/BUILD.bazel +++ b/pkg/obs/ash/BUILD.bazel @@ -42,6 +42,7 @@ go_test( name = "ash_test", srcs = [ "aggregate_test.go", + "bench_test.go", "buffer_test.go", "report_test.go", "sampler_test.go", diff --git a/pkg/obs/ash/bench_test.go b/pkg/obs/ash/bench_test.go new file mode 100644 index 000000000000..19dda7526b3f --- /dev/null +++ b/pkg/obs/ash/bench_test.go @@ -0,0 +1,48 @@ +// Copyright 2026 The Cockroach Authors. +// +// Use of this software is governed by the CockroachDB Software License +// included in the /LICENSE file. + +package ash + +import ( + "testing" + + "github.com/cockroachdb/cockroach/pkg/roachpb" +) + +// BenchmarkSetWorkState measures the per-operation cost of a +// SetWorkState + clearWorkState cycle — the pair of calls every +// registered goroutine makes on the ASH hot path. +// +// activeWorkStates is a global syncutil.Map. On high-core machines, +// concurrent Stores from different cores cause cache-line +// invalidations on the map's internal mutex and dirty-map pointer, +// degrading throughput as core count grows. This benchmark +// quantifies that degradation by varying GOMAXPROCS via -test.cpu. +// RunParallel spawns one goroutine per core, which is the right +// model: contention scales with cores executing simultaneously, +// not goroutines queued in the scheduler. +// +// See #168289 for the sharded-map proposal to address this. +// +// Example: +// +// ./dev bench pkg/obs/ash -f BenchmarkSetWorkState --test-args='-test.cpu=1,4,16,64' +func BenchmarkSetWorkState(b *testing.B) { + enabled.Store(true) + defer enabled.Store(false) + + tenantID := roachpb.MustMakeTenantID(5) + info := WorkloadInfo{WorkloadID: 12345} + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + clear := SetWorkState(tenantID, info, WorkCPU, "BenchmarkOp") + clear() + } + }) + + // Drain retired states so subsequent benchmarks start clean. + reclaimRetiredWorkStates() +} diff --git a/pkg/obs/ash/doc.go b/pkg/obs/ash/doc.go index 7cad718ecf2e..e92a007804fa 100644 --- a/pkg/obs/ash/doc.go +++ b/pkg/obs/ash/doc.go @@ -248,9 +248,8 @@ // // The on-hot-path cost is limited to SetWorkState and its cleanup // function: a sync.Pool get/put and a sync.Map store/load. Benchmarks -// show ASH adds a near-fixed ~600-700 bytes and ~17-21 allocations per -// operation with no statistically significant impact on latency or -// throughput. +// show ASH is allocation free in the steady state, and has no significant +// impact on latency or throughput. // // Plumbing workload identity also has allocation cost. // context.WithValue allocates, so context-based propagation