diff options
author | 2024-05-13 08:05:46 +0000 | |
---|---|---|
committer | 2024-05-13 08:05:46 +0000 | |
commit | c06e6fb6561595adc80ce5191640ae442771d45c (patch) | |
tree | 58845f63151eff4e984351575eea67f5e82a6c82 /internal | |
parent | [bugfix] Reset emoji fields on upload error (#2905) (diff) | |
download | gotosocial-c06e6fb6561595adc80ce5191640ae442771d45c.tar.xz |
[performance] update go-structr and go-mutexes with memory usage improvements (#2909)
* update go-structr and go-mutexes with memory usage improvements
* bump to go-structr v0.8.4
Diffstat (limited to 'internal')
-rw-r--r-- | internal/queue/simple.go | 79 |
1 files changed, 18 insertions, 61 deletions
diff --git a/internal/queue/simple.go b/internal/queue/simple.go index 0a19cfbf4..b5201f891 100644 --- a/internal/queue/simple.go +++ b/internal/queue/simple.go @@ -20,24 +20,20 @@ package queue import ( "context" "sync" + "unsafe" "codeberg.org/gruf/go-list" + "codeberg.org/gruf/go-mempool" ) -// frequency of GC cycles -// per no. unlocks. i.e. -// every 'gcfreq' unlocks. -const gcfreq = 1024 - // SimpleQueue provides a simple concurrency safe // queue using generics and a memory pool of list // elements to reduce overall memory usage. type SimpleQueue[T any] struct { l list.List[T] - p elemPool[T] + p mempool.UnsafePool w chan struct{} m sync.Mutex - n uint32 // pop counter (safely wraps around) } // Push will push given value to the queue. @@ -45,7 +41,7 @@ func (q *SimpleQueue[T]) Push(value T) { q.m.Lock() // Wrap in element. - elem := q.p.alloc() + elem := q.acquire() elem.Value = value // Push new elem to queue. @@ -75,14 +71,7 @@ func (q *SimpleQueue[T]) Pop() (value T, ok bool) { // Remove tail. q.l.Remove(tail) - q.p.free(tail) - - // Every 'gcfreq' pops perform - // a garbage collection to keep - // us squeaky clean :] - if q.n++; q.n%gcfreq == 0 { - q.p.GC() - } + q.release(tail) } q.m.Unlock() @@ -134,14 +123,7 @@ func (q *SimpleQueue[T]) PopCtx(ctx context.Context) (value T, ok bool) { // Remove element. q.l.Remove(elem) - q.p.free(elem) - - // Every 'gcfreq' pops perform - // a garbage collection to keep - // us squeaky clean :] - if q.n++; q.n%gcfreq == 0 { - q.p.GC() - } + q.release(elem) // Done with lock. q.m.Unlock() @@ -157,45 +139,20 @@ func (q *SimpleQueue[T]) Len() int { return l } -// elemPool is a very simple -// list.Elem[T] memory pool. -type elemPool[T any] struct { - current []*list.Elem[T] - victim []*list.Elem[T] -} - -func (p *elemPool[T]) alloc() *list.Elem[T] { - // First try the current queue - if l := len(p.current) - 1; l >= 0 { - mu := p.current[l] - p.current = p.current[:l] - return mu - } - - // Next try the victim queue. - if l := len(p.victim) - 1; l >= 0 { - mu := p.victim[l] - p.victim = p.victim[:l] - return mu +// acquire will acquire list elem from pool, else alloc new. +func (q *SimpleQueue[T]) acquire() *list.Elem[T] { + if ptr := q.p.Get(); ptr != nil { + return (*list.Elem[T])(ptr) } - - // Lastly, alloc new. - mu := new(list.Elem[T]) - return mu + return new(list.Elem[T]) } -// free will release given element to pool. -func (p *elemPool[T]) free(elem *list.Elem[T]) { +// release will reset list elem and release to pool. +func (q *SimpleQueue[T]) release(e *list.Elem[T]) { var zero T - elem.Next = nil - elem.Prev = nil - elem.Value = zero - p.current = append(p.current, elem) -} - -// GC will clear out unused entries from the elemPool. -func (p *elemPool[T]) GC() { - current := p.current - p.current = nil - p.victim = current + e.Next = nil + e.Prev = nil + e.Value = zero + ptr := unsafe.Pointer(e) + q.p.Put(ptr) } |