Skip to content

Commit

Permalink
perf: performance tuning (#468)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tochemey authored Sep 24, 2024
1 parent c45901e commit fb3d3a4
Show file tree
Hide file tree
Showing 13 changed files with 426 additions and 541 deletions.
80 changes: 38 additions & 42 deletions actors/behavior_stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,80 +24,78 @@

package actors

import "sync"
import (
"sync/atomic"
"unsafe"
)

// Behavior defines an actor behavior
type Behavior func(ctx *ReceiveContext)

type bnode struct {
value Behavior
previous *bnode
value Behavior
next unsafe.Pointer
}

// behaviorStack defines a stack of Behavior
type behaviorStack struct {
top *bnode
length int
mutex *sync.RWMutex
top unsafe.Pointer
length uint64
}

// newBehaviorStack creates an instance of behaviorStack
func newBehaviorStack() *behaviorStack {
return &behaviorStack{
top: nil,
length: 0,
mutex: &sync.RWMutex{},
}
}

// Len returns the length of the stack.
func (bs *behaviorStack) Len() int {
bs.mutex.RLock()
length := bs.length
bs.mutex.RUnlock()
return length
return int(atomic.LoadUint64(&bs.length))
}

// Peek helps view the top item on the stack
func (bs *behaviorStack) Peek() Behavior {
bs.mutex.RLock()
length := bs.length
bs.mutex.RUnlock()
if length == 0 {
top := atomic.LoadPointer(&bs.top)
if top == nil {
return nil
}

bs.mutex.RLock()
value := bs.top.value
bs.mutex.RUnlock()
return value
item := (*bnode)(top)
return item.value
}

// Pop removes and return top element of stack
func (bs *behaviorStack) Pop() Behavior {
bs.mutex.RLock()
length := bs.length
bs.mutex.RUnlock()
if length == 0 {
return nil
var top, next unsafe.Pointer
var item *bnode
for {
top = atomic.LoadPointer(&bs.top)
if top == nil {
return nil
}
item = (*bnode)(top)
next = atomic.LoadPointer(&item.next)
if atomic.CompareAndSwapPointer(&bs.top, top, next) {
atomic.AddUint64(&bs.length, ^uint64(0))
return item.value
}
}

bs.mutex.RLock()
n := bs.top
bs.top = n.previous
bs.length--
value := n.value
bs.mutex.RUnlock()
return value
}

// Push a new value onto the stack
func (bs *behaviorStack) Push(behavior Behavior) {
bs.mutex.Lock()
n := &bnode{behavior, bs.top}
bs.top = n
bs.length++
bs.mutex.Unlock()
node := bnode{value: behavior}
var top unsafe.Pointer
for {
top = atomic.LoadPointer(&bs.top)
node.next = top
if atomic.CompareAndSwapPointer(&bs.top, top, unsafe.Pointer(&node)) {
atomic.AddUint64(&bs.length, 1)
return
}
}
}

// IsEmpty checks if stack is empty
Expand All @@ -107,8 +105,6 @@ func (bs *behaviorStack) IsEmpty() bool {

// Reset empty the stack
func (bs *behaviorStack) Reset() {
bs.mutex.Lock()
bs.top = nil
bs.length = 0
bs.mutex.Unlock()
atomic.StorePointer(&bs.top, nil)
atomic.StoreUint64(&bs.length, 0)
}
12 changes: 1 addition & 11 deletions actors/mailbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
package actors

import (
"sync"
"sync/atomic"
"unsafe"
)
Expand All @@ -43,7 +42,6 @@ type mailbox struct {
head *node
tail *node
length int64
lock sync.Mutex
}

// newMailbox create an instance of mailbox
Expand All @@ -53,7 +51,6 @@ func newMailbox() *mailbox {
head: item,
tail: item,
length: 0,
lock: sync.Mutex{},
}
}

Expand All @@ -75,9 +72,7 @@ func (m *mailbox) Pop() *ReceiveContext {
return nil
}

m.lock.Lock()
m.tail = next
m.lock.Unlock()
value := next.value
next.value = nil
atomic.AddInt64(&m.length, -1)
Expand All @@ -90,11 +85,6 @@ func (m *mailbox) Len() int64 {
}

// IsEmpty returns true when the queue is empty
// must be called from a single, consumer goroutine
func (m *mailbox) IsEmpty() bool {
m.lock.Lock()
tail := m.tail
m.lock.Unlock()
next := (*node)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&tail.next))))
return next == nil
return atomic.LoadInt64(&m.length) == 0
}
Loading

0 comments on commit fb3d3a4

Please sign in to comment.