-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Use sync.Map and atomics for fixed bucket histograms #7474
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
f4772f3
6999929
52e2eb8
f176141
a79ec5a
a9b7e3b
8969e6c
cab55fb
d4c340c
d3e2467
b18ff3d
0de2d30
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -52,6 +52,33 @@ func TestAtomicSumAddIntConcurrentSafe(t *testing.T) { | |
| assert.Equal(t, int64(15), aSum.load()) | ||
| } | ||
|
|
||
| func BenchmarkAtomicCounter(b *testing.B) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You added a benchmark but I didn't see any results posted on PR or anywhere. Is this useful benchmark then? Or there is some policy to add benchmarks for all low-level things just in case? (reminds me of YAGNI) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok I see tha rationales #7474 (comment) Tests are great. Just 2c, but adding benchmarks without ever planning to use it (realistically) might is same as adding a dead code. They could be added when we want to execute and measure. Is it used anywhere now?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We run benchmarks as part of CI, and also do a diff on push: https://github.com/open-telemetry/opentelemetry-go/blob/main/.github/workflows/benchmark.yml. Example: https://github.com/open-telemetry/opentelemetry-go/actions/runs/18314631364 from my PR for improving sum measure performance. So if someone did make a change, we would at least be able to tell if it made performance significantly worse afterwards... |
||
| b.Run("Int64", benchmarkAtomicCounter[int64]) | ||
| b.Run("Float64", benchmarkAtomicCounter[float64]) | ||
| } | ||
|
|
||
| func benchmarkAtomicCounter[N int64 | float64](b *testing.B) { | ||
| b.Run("add", func(b *testing.B) { | ||
| var a atomicCounter[N] | ||
| b.RunParallel(func(pb *testing.PB) { | ||
| for pb.Next() { | ||
| a.add(2) | ||
| } | ||
| }) | ||
| }) | ||
| b.Run("load", func(b *testing.B) { | ||
| var a atomicCounter[N] | ||
| a.add(2) | ||
| var v N | ||
| b.RunParallel(func(pb *testing.PB) { | ||
| for pb.Next() { | ||
| v = a.load() | ||
| } | ||
| }) | ||
| assert.Equal(b, N(2), v) | ||
| }) | ||
| } | ||
|
|
||
| func TestHotColdWaitGroupConcurrentSafe(t *testing.T) { | ||
| var wg sync.WaitGroup | ||
| hcwg := &hotColdWaitGroup{} | ||
|
|
@@ -76,3 +103,150 @@ func TestHotColdWaitGroupConcurrentSafe(t *testing.T) { | |
| } | ||
| wg.Wait() | ||
| } | ||
|
|
||
| func TestAtomicN(t *testing.T) { | ||
| t.Run("Int64", testAtomicN[int64]) | ||
| t.Run("Float64", testAtomicN[float64]) | ||
| } | ||
|
|
||
| func testAtomicN[N int64 | float64](t *testing.T) { | ||
| var v atomicN[N] | ||
| assert.Equal(t, N(0), v.Load()) | ||
| assert.True(t, v.CompareAndSwap(0, 6)) | ||
| assert.Equal(t, N(6), v.Load()) | ||
| assert.False(t, v.CompareAndSwap(0, 6)) | ||
| v.Store(22) | ||
| assert.Equal(t, N(22), v.Load()) | ||
| } | ||
|
|
||
| func TestAtomicNConcurrentSafe(t *testing.T) { | ||
| t.Run("Int64", testAtomicNConcurrentSafe[int64]) | ||
| t.Run("Float64", testAtomicNConcurrentSafe[float64]) | ||
| } | ||
|
|
||
| func testAtomicNConcurrentSafe[N int64 | float64](t *testing.T) { | ||
| var wg sync.WaitGroup | ||
| var v atomicN[N] | ||
|
|
||
| for range 2 { | ||
| wg.Add(1) | ||
| go func() { | ||
| defer wg.Done() | ||
| got := v.Load() | ||
| assert.Equal(t, int64(0), int64(got)%6) | ||
| }() | ||
| wg.Add(1) | ||
| go func() { | ||
| defer wg.Done() | ||
| v.Store(12) | ||
| }() | ||
| wg.Add(1) | ||
| go func() { | ||
| defer wg.Done() | ||
| v.CompareAndSwap(0, 6) | ||
| }() | ||
| } | ||
| wg.Wait() | ||
| } | ||
|
|
||
| func BenchmarkAtomicN(b *testing.B) { | ||
| b.Run("Int64", benchmarkAtomicN[int64]) | ||
| b.Run("Float64", benchmarkAtomicN[float64]) | ||
| } | ||
|
|
||
| func benchmarkAtomicN[N int64 | float64](b *testing.B) { | ||
| b.Run("Load", func(b *testing.B) { | ||
| var a atomicN[N] | ||
| a.Store(2) | ||
| var v N | ||
| b.RunParallel(func(pb *testing.PB) { | ||
| for pb.Next() { | ||
| v = a.Load() | ||
| } | ||
| }) | ||
| assert.Equal(b, N(2), v) | ||
| }) | ||
| b.Run("Store", func(b *testing.B) { | ||
| var a atomicN[N] | ||
| b.RunParallel(func(pb *testing.PB) { | ||
| for pb.Next() { | ||
| a.Store(3) | ||
| } | ||
| }) | ||
| }) | ||
| b.Run("CompareAndSwap", func(b *testing.B) { | ||
| var a atomicN[N] | ||
| b.RunParallel(func(pb *testing.PB) { | ||
| i := 0 | ||
| for pb.Next() { | ||
| // Make sure we swap back and forth, in-case that matters. | ||
| if i%2 == 0 { | ||
| a.CompareAndSwap(0, 1) | ||
| } else { | ||
| a.CompareAndSwap(1, 0) | ||
| } | ||
| i++ | ||
| } | ||
| }) | ||
| }) | ||
| } | ||
|
|
||
| func TestAtomicMinMaxConcurrentSafe(t *testing.T) { | ||
| t.Run("Int64", testAtomicMinMaxConcurrentSafe[int64]) | ||
| t.Run("Float64", testAtomicMinMaxConcurrentSafe[float64]) | ||
| } | ||
|
|
||
| func testAtomicMinMaxConcurrentSafe[N int64 | float64](t *testing.T) { | ||
| var wg sync.WaitGroup | ||
| var minMax atomicMinMax[N] | ||
|
|
||
| assert.False(t, minMax.set.Load()) | ||
| for _, i := range []float64{2, 4, 6, 8, -3, 0, 8, 0} { | ||
| wg.Add(1) | ||
| go func() { | ||
| defer wg.Done() | ||
| minMax.Update(N(i)) | ||
| }() | ||
| } | ||
| wg.Wait() | ||
|
|
||
| assert.True(t, minMax.set.Load()) | ||
| assert.Equal(t, N(-3), minMax.minimum.Load()) | ||
| assert.Equal(t, N(8), minMax.maximum.Load()) | ||
| } | ||
|
|
||
| func BenchmarkAtomicMinMax(b *testing.B) { | ||
| b.Run("Int64", benchmarkAtomicMinMax[int64]) | ||
| b.Run("Float64", benchmarkAtomicMinMax[float64]) | ||
| } | ||
|
|
||
| func benchmarkAtomicMinMax[N int64 | float64](b *testing.B) { | ||
| b.Run("UpdateIncreasing", func(b *testing.B) { | ||
| var a atomicMinMax[N] | ||
| b.RunParallel(func(pb *testing.PB) { | ||
| i := 0 | ||
| for pb.Next() { | ||
| a.Update(N(i)) | ||
| i++ | ||
| } | ||
| }) | ||
| }) | ||
| b.Run("UpdateDecreasing", func(b *testing.B) { | ||
| var a atomicMinMax[N] | ||
| b.RunParallel(func(pb *testing.PB) { | ||
| i := 0 | ||
| for pb.Next() { | ||
| a.Update(N(i)) | ||
| i-- | ||
| } | ||
| }) | ||
| }) | ||
| b.Run("UpdateConstant", func(b *testing.B) { | ||
| var a atomicMinMax[N] | ||
| b.RunParallel(func(pb *testing.PB) { | ||
| for pb.Next() { | ||
| a.Update(N(5)) | ||
| } | ||
| }) | ||
| }) | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.