From 762af9dda26238f09c0236acaf7daf07d7961e8b Mon Sep 17 00:00:00 2001 From: Changkun Ou Date: Fri, 18 Sep 2020 16:45:01 +0200 Subject: [PATCH] optimize AtomicError (#82) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit optimize AtomicError using atomic.Value. Benchmarks: name old time/op new time/op delta AtomicError/Load-6 305ns ±11% 12ns ± 6% -96.18% (p=0.000 n=10+10) AtomicError/Set-6 314ns ±16% 14ns ± 2% -95.61% (p=0.000 n=10+9) --- core/errorx/atomicerror.go | 17 +++++------ core/errorx/atomicerror_test.go | 52 +++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/core/errorx/atomicerror.go b/core/errorx/atomicerror.go index 76f874e5..d885f489 100644 --- a/core/errorx/atomicerror.go +++ b/core/errorx/atomicerror.go @@ -1,21 +1,18 @@ package errorx -import "sync" +import "sync/atomic" type AtomicError struct { - err error - lock sync.Mutex + err atomic.Value // error } func (ae *AtomicError) Set(err error) { - ae.lock.Lock() - ae.err = err - ae.lock.Unlock() + ae.err.Store(err) } func (ae *AtomicError) Load() error { - ae.lock.Lock() - err := ae.err - ae.lock.Unlock() - return err + if v := ae.err.Load(); v != nil { + return v.(error) + } + return nil } diff --git a/core/errorx/atomicerror_test.go b/core/errorx/atomicerror_test.go index fa19cdb2..d05f8866 100644 --- a/core/errorx/atomicerror_test.go +++ b/core/errorx/atomicerror_test.go @@ -2,6 +2,8 @@ package errorx import ( "errors" + "sync" + "sync/atomic" "testing" "github.com/stretchr/testify/assert" @@ -19,3 +21,53 @@ func TestAtomicErrorNil(t *testing.T) { var err AtomicError assert.Nil(t, err.Load()) } + +func BenchmarkAtomicError(b *testing.B) { + var aerr AtomicError + wg := sync.WaitGroup{} + + b.Run("Load", func(b *testing.B) { + var done uint32 + go func() { + for { + if atomic.LoadUint32(&done) != 0 { + break + } + wg.Add(1) + go func() { + aerr.Set(errDummy) + wg.Done() + }() + } + }() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = aerr.Load() + } + b.StopTimer() + atomic.StoreUint32(&done, 1) + wg.Wait() + }) + b.Run("Set", func(b *testing.B) { + var done uint32 + go func() { + for { + if atomic.LoadUint32(&done) != 0 { + break + } + wg.Add(1) + go func() { + _ = aerr.Load() + wg.Done() + }() + } + }() + b.ResetTimer() + for i := 0; i < b.N; i++ { + aerr.Set(errDummy) + } + b.StopTimer() + atomic.StoreUint32(&done, 1) + wg.Wait() + }) +}