mirror of
https://github.com/jetkvm/kvm.git
synced 2025-09-16 08:38:14 +00:00
This change replaces all instances of GetConfig() function calls with direct access to the Config variable throughout the audio package. The modification improves performance by eliminating function call overhead and simplifies the codebase by removing unnecessary indirection. The commit also includes minor optimizations in validation logic and connection handling, while maintaining all existing functionality. Error handling remains robust with appropriate fallbacks when config values are not available. Additional improvements include: - Enhanced connection health monitoring in UnifiedAudioClient - Optimized validation functions using cached config values - Reduced memory allocations in hot paths - Improved error recovery during quality changes
330 lines
12 KiB
Go
330 lines
12 KiB
Go
package audio
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/rs/zerolog"
|
|
)
|
|
|
|
// AdaptiveOptimizer automatically adjusts audio parameters based on latency metrics
|
|
type AdaptiveOptimizer struct {
|
|
// Atomic fields MUST be first for ARM32 alignment (int64 fields need 8-byte alignment)
|
|
optimizationCount int64 // Number of optimizations performed (atomic)
|
|
lastOptimization int64 // Timestamp of last optimization (atomic)
|
|
optimizationLevel int64 // Current optimization level (0-10) (atomic)
|
|
stabilityScore int64 // Current stability score (0-100) (atomic)
|
|
optimizationInterval int64 // Current optimization interval in nanoseconds (atomic)
|
|
|
|
latencyMonitor *LatencyMonitor
|
|
bufferManager *AdaptiveBufferManager
|
|
logger zerolog.Logger
|
|
|
|
// Control channels
|
|
ctx context.Context
|
|
cancel context.CancelFunc
|
|
wg sync.WaitGroup
|
|
|
|
// Configuration
|
|
config OptimizerConfig
|
|
|
|
// Stability tracking
|
|
stabilityHistory []StabilityMetric
|
|
stabilityMutex sync.RWMutex
|
|
}
|
|
|
|
// StabilityMetric tracks system stability over time
|
|
type StabilityMetric struct {
|
|
Timestamp time.Time
|
|
LatencyStdev float64
|
|
CPUVariance float64
|
|
MemoryStable bool
|
|
ErrorRate float64
|
|
StabilityScore int
|
|
}
|
|
|
|
// OptimizerConfig holds configuration for the adaptive optimizer
|
|
type OptimizerConfig struct {
|
|
MaxOptimizationLevel int // Maximum optimization level (0-10)
|
|
CooldownPeriod time.Duration // Minimum time between optimizations
|
|
Aggressiveness float64 // How aggressively to optimize (0.0-1.0)
|
|
RollbackThreshold time.Duration // Latency threshold to rollback optimizations
|
|
StabilityPeriod time.Duration // Time to wait for stability after optimization
|
|
|
|
// Adaptive interval configuration
|
|
MinOptimizationInterval time.Duration // Minimum optimization interval (high stability)
|
|
MaxOptimizationInterval time.Duration // Maximum optimization interval (low stability)
|
|
StabilityThreshold int // Stability score threshold for interval adjustment
|
|
StabilityHistorySize int // Number of stability metrics to track
|
|
}
|
|
|
|
// DefaultOptimizerConfig returns a sensible default configuration
|
|
func DefaultOptimizerConfig() OptimizerConfig {
|
|
return OptimizerConfig{
|
|
MaxOptimizationLevel: 8,
|
|
CooldownPeriod: Config.CooldownPeriod,
|
|
Aggressiveness: Config.OptimizerAggressiveness,
|
|
RollbackThreshold: Config.RollbackThreshold,
|
|
StabilityPeriod: Config.AdaptiveOptimizerStability,
|
|
|
|
// Adaptive interval defaults
|
|
MinOptimizationInterval: 100 * time.Millisecond, // High stability: check every 100ms
|
|
MaxOptimizationInterval: 2 * time.Second, // Low stability: check every 2s
|
|
StabilityThreshold: 70, // Stability score threshold
|
|
StabilityHistorySize: 20, // Track last 20 stability metrics
|
|
}
|
|
}
|
|
|
|
// NewAdaptiveOptimizer creates a new adaptive optimizer
|
|
func NewAdaptiveOptimizer(latencyMonitor *LatencyMonitor, bufferManager *AdaptiveBufferManager, config OptimizerConfig, logger zerolog.Logger) *AdaptiveOptimizer {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
optimizer := &AdaptiveOptimizer{
|
|
latencyMonitor: latencyMonitor,
|
|
bufferManager: bufferManager,
|
|
config: config,
|
|
logger: logger.With().Str("component", "adaptive-optimizer").Logger(),
|
|
ctx: ctx,
|
|
cancel: cancel,
|
|
stabilityHistory: make([]StabilityMetric, 0, config.StabilityHistorySize),
|
|
}
|
|
|
|
// Initialize stability score and optimization interval
|
|
atomic.StoreInt64(&optimizer.stabilityScore, 50) // Start with medium stability
|
|
atomic.StoreInt64(&optimizer.optimizationInterval, int64(config.MaxOptimizationInterval))
|
|
|
|
// Register as latency monitor callback
|
|
latencyMonitor.AddOptimizationCallback(optimizer.handleLatencyOptimization)
|
|
|
|
return optimizer
|
|
}
|
|
|
|
// Start begins the adaptive optimization process
|
|
func (ao *AdaptiveOptimizer) Start() {
|
|
ao.wg.Add(1)
|
|
go ao.optimizationLoop()
|
|
ao.logger.Debug().Msg("adaptive optimizer started")
|
|
}
|
|
|
|
// Stop stops the adaptive optimizer
|
|
func (ao *AdaptiveOptimizer) Stop() {
|
|
ao.cancel()
|
|
ao.wg.Wait()
|
|
ao.logger.Debug().Msg("adaptive optimizer stopped")
|
|
}
|
|
|
|
// initializeStrategies sets up the available optimization strategies
|
|
|
|
// handleLatencyOptimization is called when latency optimization is needed
|
|
func (ao *AdaptiveOptimizer) handleLatencyOptimization(metrics LatencyMetrics) error {
|
|
currentLevel := atomic.LoadInt64(&ao.optimizationLevel)
|
|
lastOpt := atomic.LoadInt64(&ao.lastOptimization)
|
|
|
|
// Check cooldown period
|
|
if time.Since(time.Unix(0, lastOpt)) < ao.config.CooldownPeriod {
|
|
return nil
|
|
}
|
|
|
|
// Determine if we need to increase or decrease optimization level
|
|
targetLevel := ao.calculateTargetOptimizationLevel(metrics)
|
|
|
|
if targetLevel > currentLevel {
|
|
return ao.increaseOptimization(int(targetLevel))
|
|
} else if targetLevel < currentLevel {
|
|
return ao.decreaseOptimization(int(targetLevel))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// calculateTargetOptimizationLevel determines the appropriate optimization level
|
|
func (ao *AdaptiveOptimizer) calculateTargetOptimizationLevel(metrics LatencyMetrics) int64 {
|
|
// Base calculation on current latency vs target
|
|
latencyRatio := float64(metrics.Current) / float64(Config.AdaptiveOptimizerLatencyTarget) // 50ms target
|
|
|
|
// Adjust based on trend
|
|
switch metrics.Trend {
|
|
case LatencyTrendIncreasing:
|
|
latencyRatio *= 1.2 // Be more aggressive
|
|
case LatencyTrendDecreasing:
|
|
latencyRatio *= 0.8 // Be less aggressive
|
|
case LatencyTrendVolatile:
|
|
latencyRatio *= 1.1 // Slightly more aggressive
|
|
}
|
|
|
|
// Apply aggressiveness factor
|
|
latencyRatio *= ao.config.Aggressiveness
|
|
|
|
// Convert to optimization level
|
|
targetLevel := int64(latencyRatio * Config.LatencyScalingFactor) // Scale to 0-10 range
|
|
if targetLevel > int64(ao.config.MaxOptimizationLevel) {
|
|
targetLevel = int64(ao.config.MaxOptimizationLevel)
|
|
}
|
|
if targetLevel < 0 {
|
|
targetLevel = 0
|
|
}
|
|
|
|
return targetLevel
|
|
}
|
|
|
|
// increaseOptimization applies optimization strategies up to the target level
|
|
func (ao *AdaptiveOptimizer) increaseOptimization(targetLevel int) error {
|
|
atomic.StoreInt64(&ao.optimizationLevel, int64(targetLevel))
|
|
atomic.StoreInt64(&ao.lastOptimization, time.Now().UnixNano())
|
|
atomic.AddInt64(&ao.optimizationCount, 1)
|
|
|
|
return nil
|
|
}
|
|
|
|
// decreaseOptimization rolls back optimization strategies to the target level
|
|
func (ao *AdaptiveOptimizer) decreaseOptimization(targetLevel int) error {
|
|
atomic.StoreInt64(&ao.optimizationLevel, int64(targetLevel))
|
|
atomic.StoreInt64(&ao.lastOptimization, time.Now().UnixNano())
|
|
|
|
return nil
|
|
}
|
|
|
|
// optimizationLoop runs the main optimization monitoring loop
|
|
func (ao *AdaptiveOptimizer) optimizationLoop() {
|
|
defer ao.wg.Done()
|
|
|
|
// Start with initial interval
|
|
currentInterval := time.Duration(atomic.LoadInt64(&ao.optimizationInterval))
|
|
ticker := time.NewTicker(currentInterval)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-ao.ctx.Done():
|
|
return
|
|
case <-ticker.C:
|
|
// Update stability metrics and check for optimization needs
|
|
ao.updateStabilityMetrics()
|
|
ao.checkStability()
|
|
|
|
// Adjust optimization interval based on current stability
|
|
newInterval := ao.calculateOptimizationInterval()
|
|
if newInterval != currentInterval {
|
|
currentInterval = newInterval
|
|
ticker.Reset(currentInterval)
|
|
ao.logger.Debug().Dur("new_interval", currentInterval).Int64("stability_score", atomic.LoadInt64(&ao.stabilityScore)).Msg("adjusted optimization interval")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// checkStability monitors system stability and rolls back if needed
|
|
func (ao *AdaptiveOptimizer) checkStability() {
|
|
metrics := ao.latencyMonitor.GetMetrics()
|
|
|
|
// Check if we need to rollback due to excessive latency
|
|
if metrics.Current > ao.config.RollbackThreshold {
|
|
currentLevel := int(atomic.LoadInt64(&ao.optimizationLevel))
|
|
if currentLevel > 0 {
|
|
ao.logger.Warn().Dur("current_latency", metrics.Current).Dur("threshold", ao.config.RollbackThreshold).Msg("rolling back optimizations due to excessive latency")
|
|
if err := ao.decreaseOptimization(currentLevel - 1); err != nil {
|
|
ao.logger.Error().Err(err).Msg("failed to decrease optimization level")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// updateStabilityMetrics calculates and stores current system stability metrics
|
|
func (ao *AdaptiveOptimizer) updateStabilityMetrics() {
|
|
metrics := ao.latencyMonitor.GetMetrics()
|
|
|
|
// Calculate stability score based on multiple factors
|
|
stabilityScore := ao.calculateStabilityScore(metrics)
|
|
atomic.StoreInt64(&ao.stabilityScore, int64(stabilityScore))
|
|
|
|
// Store stability metric in history
|
|
stabilityMetric := StabilityMetric{
|
|
Timestamp: time.Now(),
|
|
LatencyStdev: float64(metrics.Jitter), // Use Jitter as variance indicator
|
|
CPUVariance: 0.0, // TODO: Get from system metrics
|
|
MemoryStable: true, // TODO: Get from system metrics
|
|
ErrorRate: 0.0, // TODO: Get from error tracking
|
|
StabilityScore: stabilityScore,
|
|
}
|
|
|
|
ao.stabilityMutex.Lock()
|
|
ao.stabilityHistory = append(ao.stabilityHistory, stabilityMetric)
|
|
if len(ao.stabilityHistory) > ao.config.StabilityHistorySize {
|
|
ao.stabilityHistory = ao.stabilityHistory[1:]
|
|
}
|
|
ao.stabilityMutex.Unlock()
|
|
}
|
|
|
|
// calculateStabilityScore computes a stability score (0-100) based on system metrics
|
|
func (ao *AdaptiveOptimizer) calculateStabilityScore(metrics LatencyMetrics) int {
|
|
// Base score starts at 100 (perfect stability)
|
|
score := 100.0
|
|
|
|
// Penalize high jitter (latency variance)
|
|
if metrics.Jitter > 0 && metrics.Average > 0 {
|
|
jitterRatio := float64(metrics.Jitter) / float64(metrics.Average)
|
|
variancePenalty := jitterRatio * 50 // Scale jitter impact
|
|
score -= variancePenalty
|
|
}
|
|
|
|
// Penalize latency trend volatility
|
|
switch metrics.Trend {
|
|
case LatencyTrendVolatile:
|
|
score -= 20
|
|
case LatencyTrendIncreasing:
|
|
score -= 10
|
|
case LatencyTrendDecreasing:
|
|
score += 5 // Slight bonus for improving latency
|
|
}
|
|
|
|
// Ensure score is within bounds
|
|
if score < 0 {
|
|
score = 0
|
|
}
|
|
if score > 100 {
|
|
score = 100
|
|
}
|
|
|
|
return int(score)
|
|
}
|
|
|
|
// calculateOptimizationInterval determines the optimization interval based on stability
|
|
func (ao *AdaptiveOptimizer) calculateOptimizationInterval() time.Duration {
|
|
stabilityScore := atomic.LoadInt64(&ao.stabilityScore)
|
|
|
|
// High stability = shorter intervals (more frequent optimization)
|
|
// Low stability = longer intervals (less frequent optimization)
|
|
if stabilityScore >= int64(ao.config.StabilityThreshold) {
|
|
// High stability: use minimum interval
|
|
interval := ao.config.MinOptimizationInterval
|
|
atomic.StoreInt64(&ao.optimizationInterval, int64(interval))
|
|
return interval
|
|
} else {
|
|
// Low stability: scale interval based on stability score
|
|
// Lower stability = longer intervals
|
|
stabilityRatio := float64(stabilityScore) / float64(ao.config.StabilityThreshold)
|
|
minInterval := float64(ao.config.MinOptimizationInterval)
|
|
maxInterval := float64(ao.config.MaxOptimizationInterval)
|
|
|
|
// Linear interpolation between min and max intervals
|
|
interval := time.Duration(minInterval + (maxInterval-minInterval)*(1.0-stabilityRatio))
|
|
atomic.StoreInt64(&ao.optimizationInterval, int64(interval))
|
|
return interval
|
|
}
|
|
}
|
|
|
|
// GetOptimizationStats returns current optimization statistics
|
|
func (ao *AdaptiveOptimizer) GetOptimizationStats() map[string]interface{} {
|
|
return map[string]interface{}{
|
|
"optimization_level": atomic.LoadInt64(&ao.optimizationLevel),
|
|
"optimization_count": atomic.LoadInt64(&ao.optimizationCount),
|
|
"last_optimization": time.Unix(0, atomic.LoadInt64(&ao.lastOptimization)),
|
|
"stability_score": atomic.LoadInt64(&ao.stabilityScore),
|
|
"optimization_interval": time.Duration(atomic.LoadInt64(&ao.optimizationInterval)),
|
|
}
|
|
}
|
|
|
|
// Strategy implementation methods (stubs for now)
|