mirror of https://github.com/jetkvm/kvm.git
205 lines
4.8 KiB
Go
205 lines
4.8 KiB
Go
package audio
|
|
|
|
import (
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
// AtomicCounter provides thread-safe counter operations
|
|
type AtomicCounter struct {
|
|
value int64
|
|
}
|
|
|
|
// NewAtomicCounter creates a new atomic counter
|
|
func NewAtomicCounter() *AtomicCounter {
|
|
return &AtomicCounter{}
|
|
}
|
|
|
|
// Add atomically adds delta to the counter and returns the new value
|
|
func (c *AtomicCounter) Add(delta int64) int64 {
|
|
return atomic.AddInt64(&c.value, delta)
|
|
}
|
|
|
|
// Increment atomically increments the counter by 1
|
|
func (c *AtomicCounter) Increment() int64 {
|
|
return atomic.AddInt64(&c.value, 1)
|
|
}
|
|
|
|
// Load atomically loads the counter value
|
|
func (c *AtomicCounter) Load() int64 {
|
|
return atomic.LoadInt64(&c.value)
|
|
}
|
|
|
|
// Store atomically stores a new value
|
|
func (c *AtomicCounter) Store(value int64) {
|
|
atomic.StoreInt64(&c.value, value)
|
|
}
|
|
|
|
// Reset atomically resets the counter to zero
|
|
func (c *AtomicCounter) Reset() {
|
|
atomic.StoreInt64(&c.value, 0)
|
|
}
|
|
|
|
// Swap atomically swaps the value and returns the old value
|
|
func (c *AtomicCounter) Swap(new int64) int64 {
|
|
return atomic.SwapInt64(&c.value, new)
|
|
}
|
|
|
|
// FrameMetrics provides common frame tracking metrics
|
|
type FrameMetrics struct {
|
|
Total *AtomicCounter
|
|
Dropped *AtomicCounter
|
|
Bytes *AtomicCounter
|
|
}
|
|
|
|
// NewFrameMetrics creates a new frame metrics tracker
|
|
func NewFrameMetrics() *FrameMetrics {
|
|
return &FrameMetrics{
|
|
Total: NewAtomicCounter(),
|
|
Dropped: NewAtomicCounter(),
|
|
Bytes: NewAtomicCounter(),
|
|
}
|
|
}
|
|
|
|
// RecordFrame atomically records a successful frame with its size
|
|
func (fm *FrameMetrics) RecordFrame(size int64) {
|
|
fm.Total.Increment()
|
|
fm.Bytes.Add(size)
|
|
}
|
|
|
|
// RecordDrop atomically records a dropped frame
|
|
func (fm *FrameMetrics) RecordDrop() {
|
|
fm.Dropped.Increment()
|
|
}
|
|
|
|
// GetStats returns current metrics values
|
|
func (fm *FrameMetrics) GetStats() (total, dropped, bytes int64) {
|
|
return fm.Total.Load(), fm.Dropped.Load(), fm.Bytes.Load()
|
|
}
|
|
|
|
// Reset resets all metrics to zero
|
|
func (fm *FrameMetrics) Reset() {
|
|
fm.Total.Reset()
|
|
fm.Dropped.Reset()
|
|
fm.Bytes.Reset()
|
|
}
|
|
|
|
// GetDropRate calculates the drop rate as a percentage
|
|
func (fm *FrameMetrics) GetDropRate() float64 {
|
|
total := fm.Total.Load()
|
|
if total == 0 {
|
|
return 0.0
|
|
}
|
|
dropped := fm.Dropped.Load()
|
|
return float64(dropped) / float64(total) * 100.0
|
|
}
|
|
|
|
// LatencyTracker provides atomic latency tracking
|
|
type LatencyTracker struct {
|
|
current *AtomicCounter
|
|
min *AtomicCounter
|
|
max *AtomicCounter
|
|
average *AtomicCounter
|
|
samples *AtomicCounter
|
|
}
|
|
|
|
// NewLatencyTracker creates a new latency tracker
|
|
func NewLatencyTracker() *LatencyTracker {
|
|
lt := &LatencyTracker{
|
|
current: NewAtomicCounter(),
|
|
min: NewAtomicCounter(),
|
|
max: NewAtomicCounter(),
|
|
average: NewAtomicCounter(),
|
|
samples: NewAtomicCounter(),
|
|
}
|
|
// Initialize min to max value so first measurement sets it properly
|
|
lt.min.Store(int64(^uint64(0) >> 1)) // Max int64
|
|
return lt
|
|
}
|
|
|
|
// RecordLatency atomically records a new latency measurement
|
|
func (lt *LatencyTracker) RecordLatency(latency time.Duration) {
|
|
latencyNanos := latency.Nanoseconds()
|
|
lt.current.Store(latencyNanos)
|
|
lt.samples.Increment()
|
|
|
|
// Update min
|
|
for {
|
|
oldMin := lt.min.Load()
|
|
if latencyNanos >= oldMin {
|
|
break
|
|
}
|
|
if atomic.CompareAndSwapInt64(<.min.value, oldMin, latencyNanos) {
|
|
break
|
|
}
|
|
}
|
|
|
|
// Update max
|
|
for {
|
|
oldMax := lt.max.Load()
|
|
if latencyNanos <= oldMax {
|
|
break
|
|
}
|
|
if atomic.CompareAndSwapInt64(<.max.value, oldMax, latencyNanos) {
|
|
break
|
|
}
|
|
}
|
|
|
|
// Update average using exponential moving average
|
|
oldAvg := lt.average.Load()
|
|
newAvg := (oldAvg*7 + latencyNanos) / 8 // 87.5% weight to old average
|
|
lt.average.Store(newAvg)
|
|
}
|
|
|
|
// GetLatencyStats returns current latency statistics
|
|
func (lt *LatencyTracker) GetLatencyStats() (current, min, max, average time.Duration, samples int64) {
|
|
return time.Duration(lt.current.Load()),
|
|
time.Duration(lt.min.Load()),
|
|
time.Duration(lt.max.Load()),
|
|
time.Duration(lt.average.Load()),
|
|
lt.samples.Load()
|
|
}
|
|
|
|
// PoolMetrics provides common pool performance metrics
|
|
type PoolMetrics struct {
|
|
Hits *AtomicCounter
|
|
Misses *AtomicCounter
|
|
}
|
|
|
|
// NewPoolMetrics creates a new pool metrics tracker
|
|
func NewPoolMetrics() *PoolMetrics {
|
|
return &PoolMetrics{
|
|
Hits: NewAtomicCounter(),
|
|
Misses: NewAtomicCounter(),
|
|
}
|
|
}
|
|
|
|
// RecordHit atomically records a pool hit
|
|
func (pm *PoolMetrics) RecordHit() {
|
|
pm.Hits.Increment()
|
|
}
|
|
|
|
// RecordMiss atomically records a pool miss
|
|
func (pm *PoolMetrics) RecordMiss() {
|
|
pm.Misses.Increment()
|
|
}
|
|
|
|
// GetHitRate calculates the hit rate as a percentage
|
|
func (pm *PoolMetrics) GetHitRate() float64 {
|
|
hits := pm.Hits.Load()
|
|
misses := pm.Misses.Load()
|
|
total := hits + misses
|
|
if total == 0 {
|
|
return 0.0
|
|
}
|
|
return float64(hits) / float64(total) * 100.0
|
|
}
|
|
|
|
// GetStats returns hit and miss counts
|
|
func (pm *PoolMetrics) GetStats() (hits, misses int64, hitRate float64) {
|
|
hits = pm.Hits.Load()
|
|
misses = pm.Misses.Load()
|
|
hitRate = pm.GetHitRate()
|
|
return
|
|
}
|