feat: create v2-prep branch with comprehensive planning
Restructured project for V2 refactor: **Structure Changes:** - Moved all V1 code to orig/ folder (preserved with git mv) - Created docs/planning/ directory - Added orig/README_V1.md explaining V1 preservation **Planning Documents:** - 00_V2_MASTER_PLAN.md: Complete architecture overview - Executive summary of critical V1 issues - High-level component architecture diagrams - 5-phase implementation roadmap - Success metrics and risk mitigation - 07_TASK_BREAKDOWN.md: Atomic task breakdown - 99+ hours of detailed tasks - Every task < 2 hours (atomic) - Clear dependencies and success criteria - Organized by implementation phase **V2 Key Improvements:** - Per-exchange parsers (factory pattern) - Multi-layer strict validation - Multi-index pool cache - Background validation pipeline - Comprehensive observability **Critical Issues Addressed:** - Zero address tokens (strict validation + cache enrichment) - Parsing accuracy (protocol-specific parsers) - No audit trail (background validation channel) - Inefficient lookups (multi-index cache) - Stats disconnection (event-driven metrics) Next Steps: 1. Review planning documents 2. Begin Phase 1: Foundation (P1-001 through P1-010) 3. Implement parsers in Phase 2 4. Build cache system in Phase 3 5. Add validation pipeline in Phase 4 6. Migrate and test in Phase 5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
502
orig/pkg/performance/optimizer.go
Normal file
502
orig/pkg/performance/optimizer.go
Normal file
@@ -0,0 +1,502 @@
|
||||
package performance
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fraktal/mev-beta/internal/logger"
|
||||
)
|
||||
|
||||
// PerformanceOptimizer implements various performance optimization strategies
|
||||
type PerformanceOptimizer struct {
|
||||
logger *logger.Logger
|
||||
|
||||
// Connection pooling
|
||||
connectionPools map[string]*ConnectionPool
|
||||
poolMutex sync.RWMutex
|
||||
|
||||
// Adaptive worker scaling
|
||||
workerManager *AdaptiveWorkerManager
|
||||
|
||||
// Smart caching
|
||||
cacheManager *SmartCacheManager
|
||||
|
||||
// Metrics collection
|
||||
metrics *PerformanceMetrics
|
||||
}
|
||||
|
||||
// ConnectionPool manages a pool of reusable connections
|
||||
type ConnectionPool struct {
|
||||
connections chan interface{}
|
||||
maxSize int
|
||||
currentSize int
|
||||
factory func() (interface{}, error)
|
||||
cleanup func(interface{}) error
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
// AdaptiveWorkerManager manages dynamic worker scaling
|
||||
type AdaptiveWorkerManager struct {
|
||||
currentWorkers int
|
||||
maxWorkers int
|
||||
minWorkers int
|
||||
targetLatency time.Duration
|
||||
scaleUpThreshold float64
|
||||
scaleDownThreshold float64
|
||||
lastScaleAction time.Time
|
||||
cooldownPeriod time.Duration
|
||||
metrics *WorkerMetrics
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
// SmartCacheManager implements intelligent caching with TTL and invalidation
|
||||
type SmartCacheManager struct {
|
||||
caches map[string]*CacheInstance
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
// CacheInstance represents a single cache with TTL and size limits
|
||||
type CacheInstance struct {
|
||||
data map[string]*CacheEntry
|
||||
maxSize int
|
||||
defaultTTL time.Duration
|
||||
hits uint64
|
||||
misses uint64
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
// CacheEntry represents a cached item
|
||||
type CacheEntry struct {
|
||||
value interface{}
|
||||
expiry time.Time
|
||||
lastAccess time.Time
|
||||
accessCount uint64
|
||||
}
|
||||
|
||||
// PerformanceMetrics tracks various performance metrics
|
||||
type PerformanceMetrics struct {
|
||||
TotalRequests uint64
|
||||
SuccessfulRequests uint64
|
||||
FailedRequests uint64
|
||||
AverageLatency time.Duration
|
||||
P95Latency time.Duration
|
||||
P99Latency time.Duration
|
||||
CacheHitRatio float64
|
||||
ActiveConnections int
|
||||
ActiveWorkers int
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
// WorkerMetrics tracks worker performance
|
||||
type WorkerMetrics struct {
|
||||
TasksProcessed uint64
|
||||
AverageTaskTime time.Duration
|
||||
QueueSize int
|
||||
WorkerUtilization float64
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
// NewPerformanceOptimizer creates a new performance optimizer
|
||||
func NewPerformanceOptimizer(logger *logger.Logger) *PerformanceOptimizer {
|
||||
return &PerformanceOptimizer{
|
||||
logger: logger,
|
||||
connectionPools: make(map[string]*ConnectionPool),
|
||||
workerManager: NewAdaptiveWorkerManager(10, 100, 2, 100*time.Millisecond),
|
||||
cacheManager: NewSmartCacheManager(),
|
||||
metrics: &PerformanceMetrics{},
|
||||
}
|
||||
}
|
||||
|
||||
// NewConnectionPool creates a new connection pool
|
||||
func NewConnectionPool(maxSize int, factory func() (interface{}, error), cleanup func(interface{}) error) *ConnectionPool {
|
||||
return &ConnectionPool{
|
||||
connections: make(chan interface{}, maxSize),
|
||||
maxSize: maxSize,
|
||||
factory: factory,
|
||||
cleanup: cleanup,
|
||||
}
|
||||
}
|
||||
|
||||
// Get retrieves a connection from the pool
|
||||
func (cp *ConnectionPool) Get() (interface{}, error) {
|
||||
select {
|
||||
case conn := <-cp.connections:
|
||||
return conn, nil
|
||||
default:
|
||||
// No available connections, create new one
|
||||
cp.mutex.Lock()
|
||||
defer cp.mutex.Unlock()
|
||||
|
||||
if cp.currentSize < cp.maxSize {
|
||||
conn, err := cp.factory()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cp.currentSize++
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// Pool is full, wait for available connection
|
||||
return <-cp.connections, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Put returns a connection to the pool
|
||||
func (cp *ConnectionPool) Put(conn interface{}) {
|
||||
select {
|
||||
case cp.connections <- conn:
|
||||
// Successfully returned to pool
|
||||
default:
|
||||
// Pool is full, cleanup the connection
|
||||
if cp.cleanup != nil {
|
||||
cp.cleanup(conn)
|
||||
}
|
||||
cp.mutex.Lock()
|
||||
cp.currentSize--
|
||||
cp.mutex.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// NewAdaptiveWorkerManager creates a new adaptive worker manager
|
||||
func NewAdaptiveWorkerManager(current, max, min int, targetLatency time.Duration) *AdaptiveWorkerManager {
|
||||
return &AdaptiveWorkerManager{
|
||||
currentWorkers: current,
|
||||
maxWorkers: max,
|
||||
minWorkers: min,
|
||||
targetLatency: targetLatency,
|
||||
scaleUpThreshold: 1.5, // Scale up if latency > 1.5x target
|
||||
scaleDownThreshold: 0.7, // Scale down if latency < 0.7x target
|
||||
cooldownPeriod: 30 * time.Second,
|
||||
metrics: &WorkerMetrics{},
|
||||
}
|
||||
}
|
||||
|
||||
// AdjustWorkerCount adjusts the number of workers based on current performance
|
||||
func (awm *AdaptiveWorkerManager) AdjustWorkerCount(currentLatency time.Duration, queueSize int) int {
|
||||
awm.mutex.Lock()
|
||||
defer awm.mutex.Unlock()
|
||||
|
||||
// Check cooldown period
|
||||
if time.Since(awm.lastScaleAction) < awm.cooldownPeriod {
|
||||
return awm.currentWorkers
|
||||
}
|
||||
|
||||
latencyRatio := float64(currentLatency) / float64(awm.targetLatency)
|
||||
|
||||
// Scale up if latency is too high or queue is building up
|
||||
if latencyRatio > awm.scaleUpThreshold || queueSize > awm.currentWorkers*2 {
|
||||
if awm.currentWorkers < awm.maxWorkers {
|
||||
newCount := awm.currentWorkers + (awm.currentWorkers / 4) // Increase by 25%
|
||||
if newCount > awm.maxWorkers {
|
||||
newCount = awm.maxWorkers
|
||||
}
|
||||
awm.currentWorkers = newCount
|
||||
awm.lastScaleAction = time.Now()
|
||||
return newCount
|
||||
}
|
||||
}
|
||||
|
||||
// Scale down if latency is too low and queue is empty
|
||||
if latencyRatio < awm.scaleDownThreshold && queueSize == 0 {
|
||||
if awm.currentWorkers > awm.minWorkers {
|
||||
newCount := awm.currentWorkers - (awm.currentWorkers / 6) // Decrease by ~16%
|
||||
if newCount < awm.minWorkers {
|
||||
newCount = awm.minWorkers
|
||||
}
|
||||
awm.currentWorkers = newCount
|
||||
awm.lastScaleAction = time.Now()
|
||||
return newCount
|
||||
}
|
||||
}
|
||||
|
||||
return awm.currentWorkers
|
||||
}
|
||||
|
||||
// NewSmartCacheManager creates a new smart cache manager
|
||||
func NewSmartCacheManager() *SmartCacheManager {
|
||||
return &SmartCacheManager{
|
||||
caches: make(map[string]*CacheInstance),
|
||||
}
|
||||
}
|
||||
|
||||
// GetCache retrieves or creates a cache instance
|
||||
func (scm *SmartCacheManager) GetCache(name string, maxSize int, defaultTTL time.Duration) *CacheInstance {
|
||||
scm.mutex.RLock()
|
||||
if cache, exists := scm.caches[name]; exists {
|
||||
scm.mutex.RUnlock()
|
||||
return cache
|
||||
}
|
||||
scm.mutex.RUnlock()
|
||||
|
||||
scm.mutex.Lock()
|
||||
defer scm.mutex.Unlock()
|
||||
|
||||
// Double-check after acquiring write lock
|
||||
if cache, exists := scm.caches[name]; exists {
|
||||
return cache
|
||||
}
|
||||
|
||||
cache := &CacheInstance{
|
||||
data: make(map[string]*CacheEntry),
|
||||
maxSize: maxSize,
|
||||
defaultTTL: defaultTTL,
|
||||
}
|
||||
scm.caches[name] = cache
|
||||
|
||||
// Start cleanup routine for this cache
|
||||
go cache.startCleanup()
|
||||
|
||||
return cache
|
||||
}
|
||||
|
||||
// Get retrieves a value from the cache
|
||||
func (ci *CacheInstance) Get(key string) (interface{}, bool) {
|
||||
ci.mutex.RLock()
|
||||
defer ci.mutex.RUnlock()
|
||||
|
||||
entry, exists := ci.data[key]
|
||||
if !exists {
|
||||
ci.misses++
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Check if expired
|
||||
if time.Now().After(entry.expiry) {
|
||||
ci.mutex.RUnlock()
|
||||
ci.mutex.Lock()
|
||||
delete(ci.data, key)
|
||||
ci.mutex.Unlock()
|
||||
ci.mutex.RLock()
|
||||
ci.misses++
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Update access statistics
|
||||
entry.lastAccess = time.Now()
|
||||
entry.accessCount++
|
||||
ci.hits++
|
||||
|
||||
return entry.value, true
|
||||
}
|
||||
|
||||
// Set stores a value in the cache
|
||||
func (ci *CacheInstance) Set(key string, value interface{}) {
|
||||
ci.SetWithTTL(key, value, ci.defaultTTL)
|
||||
}
|
||||
|
||||
// SetWithTTL stores a value with custom TTL
|
||||
func (ci *CacheInstance) SetWithTTL(key string, value interface{}, ttl time.Duration) {
|
||||
ci.mutex.Lock()
|
||||
defer ci.mutex.Unlock()
|
||||
|
||||
// Check if we need to evict items
|
||||
if len(ci.data) >= ci.maxSize {
|
||||
ci.evictLRU()
|
||||
}
|
||||
|
||||
ci.data[key] = &CacheEntry{
|
||||
value: value,
|
||||
expiry: time.Now().Add(ttl),
|
||||
lastAccess: time.Now(),
|
||||
accessCount: 1,
|
||||
}
|
||||
}
|
||||
|
||||
// evictLRU evicts the least recently used item
|
||||
func (ci *CacheInstance) evictLRU() {
|
||||
var oldestKey string
|
||||
var oldestTime time.Time
|
||||
|
||||
for key, entry := range ci.data {
|
||||
if oldestKey == "" || entry.lastAccess.Before(oldestTime) {
|
||||
oldestKey = key
|
||||
oldestTime = entry.lastAccess
|
||||
}
|
||||
}
|
||||
|
||||
if oldestKey != "" {
|
||||
delete(ci.data, oldestKey)
|
||||
}
|
||||
}
|
||||
|
||||
// startCleanup starts the cleanup routine for expired entries
|
||||
func (ci *CacheInstance) startCleanup() {
|
||||
ticker := time.NewTicker(5 * time.Minute)
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C {
|
||||
ci.cleanupExpired()
|
||||
}
|
||||
}
|
||||
|
||||
// cleanupExpired removes expired entries
|
||||
func (ci *CacheInstance) cleanupExpired() {
|
||||
ci.mutex.Lock()
|
||||
defer ci.mutex.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
for key, entry := range ci.data {
|
||||
if now.After(entry.expiry) {
|
||||
delete(ci.data, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetHitRatio returns the cache hit ratio
|
||||
func (ci *CacheInstance) GetHitRatio() float64 {
|
||||
ci.mutex.RLock()
|
||||
defer ci.mutex.RUnlock()
|
||||
|
||||
total := ci.hits + ci.misses
|
||||
if total == 0 {
|
||||
return 0
|
||||
}
|
||||
return float64(ci.hits) / float64(total)
|
||||
}
|
||||
|
||||
// OptimizeForRealTime implements real-time processing optimizations
|
||||
func (po *PerformanceOptimizer) OptimizeForRealTime(ctx context.Context) {
|
||||
// Create connection pools for RPC endpoints
|
||||
po.createRPCConnectionPools()
|
||||
|
||||
// Start adaptive worker management
|
||||
go po.manageWorkerAdaptation(ctx)
|
||||
|
||||
// Start cache warming
|
||||
go po.warmCaches(ctx)
|
||||
|
||||
// Start metrics collection
|
||||
go po.collectMetrics(ctx)
|
||||
|
||||
po.logger.Info("Performance optimization started for real-time processing")
|
||||
}
|
||||
|
||||
// createRPCConnectionPools creates connection pools for RPC endpoints
|
||||
func (po *PerformanceOptimizer) createRPCConnectionPools() {
|
||||
// Create pool for Arbitrum RPC connections
|
||||
arbitrumPool := NewConnectionPool(
|
||||
20, // Max 20 connections
|
||||
func() (interface{}, error) {
|
||||
// Factory function to create new RPC connection
|
||||
// In production, this would create an actual ethclient connection
|
||||
return "rpc_connection", nil
|
||||
},
|
||||
func(conn interface{}) error {
|
||||
// Cleanup function to close connection
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
po.poolMutex.Lock()
|
||||
po.connectionPools["arbitrum_rpc"] = arbitrumPool
|
||||
po.poolMutex.Unlock()
|
||||
|
||||
po.logger.Info("Created RPC connection pools")
|
||||
}
|
||||
|
||||
// manageWorkerAdaptation manages adaptive worker scaling
|
||||
func (po *PerformanceOptimizer) manageWorkerAdaptation(ctx context.Context) {
|
||||
ticker := time.NewTicker(10 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
// Get current metrics
|
||||
currentLatency := po.metrics.AverageLatency
|
||||
queueSize := 0 // This would be obtained from actual queue
|
||||
|
||||
// Adjust worker count
|
||||
newWorkerCount := po.workerManager.AdjustWorkerCount(currentLatency, queueSize)
|
||||
|
||||
po.logger.Debug(fmt.Sprintf("Adaptive worker scaling: %d workers (latency: %v, queue: %d)",
|
||||
newWorkerCount, currentLatency, queueSize))
|
||||
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// warmCaches preloads frequently accessed data into caches
|
||||
func (po *PerformanceOptimizer) warmCaches(ctx context.Context) {
|
||||
poolCache := po.cacheManager.GetCache("pools", 1000, 5*time.Minute)
|
||||
|
||||
// Warm up with common pool addresses
|
||||
commonPools := []string{
|
||||
"0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640", // USDC/WETH V3
|
||||
"0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc", // USDC/WETH V2
|
||||
"0x17c14D2c404D167802b16C450d3c99F88F2c4F4d", // USDC/WETH V3 0.3%
|
||||
}
|
||||
|
||||
for _, pool := range commonPools {
|
||||
// In production, this would fetch real pool data
|
||||
poolCache.Set(pool, map[string]interface{}{
|
||||
"warmed": true,
|
||||
"timestamp": time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
po.logger.Info("Cache warming completed")
|
||||
}
|
||||
|
||||
// collectMetrics collects and reports performance metrics
|
||||
func (po *PerformanceOptimizer) collectMetrics(ctx context.Context) {
|
||||
ticker := time.NewTicker(30 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
po.reportMetrics()
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reportMetrics reports current performance metrics
|
||||
func (po *PerformanceOptimizer) reportMetrics() {
|
||||
po.metrics.mutex.RLock()
|
||||
defer po.metrics.mutex.RUnlock()
|
||||
|
||||
// Calculate cache hit ratios
|
||||
totalHitRatio := 0.0
|
||||
cacheCount := 0
|
||||
|
||||
po.cacheManager.mutex.RLock()
|
||||
for name, cache := range po.cacheManager.caches {
|
||||
hitRatio := cache.GetHitRatio()
|
||||
totalHitRatio += hitRatio
|
||||
cacheCount++
|
||||
|
||||
po.logger.Debug(fmt.Sprintf("Cache %s hit ratio: %.2f%%", name, hitRatio*100))
|
||||
}
|
||||
po.cacheManager.mutex.RUnlock()
|
||||
|
||||
if cacheCount > 0 {
|
||||
po.metrics.CacheHitRatio = totalHitRatio / float64(cacheCount)
|
||||
}
|
||||
|
||||
po.logger.Info(fmt.Sprintf("🚀 PERFORMANCE METRICS:"))
|
||||
po.logger.Info(fmt.Sprintf(" Average Latency: %v", po.metrics.AverageLatency))
|
||||
po.logger.Info(fmt.Sprintf(" Cache Hit Ratio: %.2f%%", po.metrics.CacheHitRatio*100))
|
||||
po.logger.Info(fmt.Sprintf(" Active Workers: %d", po.workerManager.currentWorkers))
|
||||
po.logger.Info(fmt.Sprintf(" Total Requests: %d", po.metrics.TotalRequests))
|
||||
po.logger.Info(fmt.Sprintf(" Success Rate: %.2f%%",
|
||||
float64(po.metrics.SuccessfulRequests)/float64(po.metrics.TotalRequests)*100))
|
||||
}
|
||||
|
||||
// GetConnectionPool retrieves a connection pool by name
|
||||
func (po *PerformanceOptimizer) GetConnectionPool(name string) *ConnectionPool {
|
||||
po.poolMutex.RLock()
|
||||
defer po.poolMutex.RUnlock()
|
||||
return po.connectionPools[name]
|
||||
}
|
||||
|
||||
// GetCache retrieves a cache instance
|
||||
func (po *PerformanceOptimizer) GetCache(name string, maxSize int, defaultTTL time.Duration) *CacheInstance {
|
||||
return po.cacheManager.GetCache(name, maxSize, defaultTTL)
|
||||
}
|
||||
392
orig/pkg/performance/pools.go
Normal file
392
orig/pkg/performance/pools.go
Normal file
@@ -0,0 +1,392 @@
|
||||
package performance
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/holiman/uint256"
|
||||
|
||||
"github.com/fraktal/mev-beta/pkg/events"
|
||||
)
|
||||
|
||||
// ObjectPool manages reusable objects to reduce garbage collection pressure
|
||||
type ObjectPool struct {
|
||||
bigIntPool sync.Pool
|
||||
uint256Pool sync.Pool
|
||||
eventPool sync.Pool
|
||||
addressPool sync.Pool
|
||||
slicePool sync.Pool
|
||||
}
|
||||
|
||||
// NewObjectPool creates a new object pool for performance optimization
|
||||
func NewObjectPool() *ObjectPool {
|
||||
return &ObjectPool{
|
||||
bigIntPool: sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(big.Int)
|
||||
},
|
||||
},
|
||||
uint256Pool: sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(uint256.Int)
|
||||
},
|
||||
},
|
||||
eventPool: sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &events.Event{}
|
||||
},
|
||||
},
|
||||
addressPool: sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]common.Address, 0, 8)
|
||||
},
|
||||
},
|
||||
slicePool: sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, 0, 1024)
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GetBigInt returns a reusable big.Int from the pool
|
||||
func (p *ObjectPool) GetBigInt() *big.Int {
|
||||
bi := p.bigIntPool.Get().(*big.Int)
|
||||
bi.SetInt64(0) // Reset to zero
|
||||
return bi
|
||||
}
|
||||
|
||||
// PutBigInt returns a big.Int to the pool for reuse
|
||||
func (p *ObjectPool) PutBigInt(bi *big.Int) {
|
||||
if bi != nil {
|
||||
p.bigIntPool.Put(bi)
|
||||
}
|
||||
}
|
||||
|
||||
// GetUint256 returns a reusable uint256.Int from the pool
|
||||
func (p *ObjectPool) GetUint256() *uint256.Int {
|
||||
ui := p.uint256Pool.Get().(*uint256.Int)
|
||||
ui.SetUint64(0) // Reset to zero
|
||||
return ui
|
||||
}
|
||||
|
||||
// PutUint256 returns a uint256.Int to the pool for reuse
|
||||
func (p *ObjectPool) PutUint256(ui *uint256.Int) {
|
||||
if ui != nil {
|
||||
p.uint256Pool.Put(ui)
|
||||
}
|
||||
}
|
||||
|
||||
// GetEvent returns a reusable Event from the pool
|
||||
func (p *ObjectPool) GetEvent() *events.Event {
|
||||
event := p.eventPool.Get().(*events.Event)
|
||||
// Reset event fields
|
||||
*event = events.Event{}
|
||||
return event
|
||||
}
|
||||
|
||||
// PutEvent returns an Event to the pool for reuse
|
||||
func (p *ObjectPool) PutEvent(event *events.Event) {
|
||||
if event != nil {
|
||||
p.eventPool.Put(event)
|
||||
}
|
||||
}
|
||||
|
||||
// GetAddressSlice returns a reusable address slice from the pool
|
||||
func (p *ObjectPool) GetAddressSlice() []common.Address {
|
||||
slice := p.addressPool.Get().([]common.Address)
|
||||
return slice[:0] // Reset length to 0 but keep capacity
|
||||
}
|
||||
|
||||
// PutAddressSlice returns an address slice to the pool for reuse
|
||||
func (p *ObjectPool) PutAddressSlice(slice []common.Address) {
|
||||
if slice != nil && cap(slice) > 0 {
|
||||
p.addressPool.Put(slice)
|
||||
}
|
||||
}
|
||||
|
||||
// GetByteSlice returns a reusable byte slice from the pool
|
||||
func (p *ObjectPool) GetByteSlice() []byte {
|
||||
slice := p.slicePool.Get().([]byte)
|
||||
return slice[:0] // Reset length to 0 but keep capacity
|
||||
}
|
||||
|
||||
// PutByteSlice returns a byte slice to the pool for reuse
|
||||
func (p *ObjectPool) PutByteSlice(slice []byte) {
|
||||
if slice != nil && cap(slice) > 0 {
|
||||
p.slicePool.Put(slice)
|
||||
}
|
||||
}
|
||||
|
||||
// LockFreeRingBuffer implements a lock-free ring buffer for high-performance message passing
|
||||
type LockFreeRingBuffer struct {
|
||||
buffer []interface{}
|
||||
mask uint64
|
||||
head uint64 // Padding to prevent false sharing
|
||||
_ [7]uint64
|
||||
tail uint64 // Padding to prevent false sharing
|
||||
_ [7]uint64
|
||||
}
|
||||
|
||||
// NewLockFreeRingBuffer creates a new lock-free ring buffer
|
||||
// Size must be a power of 2
|
||||
func NewLockFreeRingBuffer(size uint64) *LockFreeRingBuffer {
|
||||
// Ensure size is power of 2
|
||||
if size&(size-1) != 0 {
|
||||
// Find next power of 2
|
||||
size = 1 << (64 - countLeadingZeros(size-1))
|
||||
}
|
||||
|
||||
return &LockFreeRingBuffer{
|
||||
buffer: make([]interface{}, size),
|
||||
mask: size - 1,
|
||||
}
|
||||
}
|
||||
|
||||
// countLeadingZeros counts leading zeros in a uint64
|
||||
func countLeadingZeros(x uint64) int {
|
||||
if x == 0 {
|
||||
return 64
|
||||
}
|
||||
n := 0
|
||||
if x <= 0x00000000FFFFFFFF {
|
||||
n += 32
|
||||
x <<= 32
|
||||
}
|
||||
if x <= 0x0000FFFFFFFFFFFF {
|
||||
n += 16
|
||||
x <<= 16
|
||||
}
|
||||
if x <= 0x00FFFFFFFFFFFFFF {
|
||||
n += 8
|
||||
x <<= 8
|
||||
}
|
||||
if x <= 0x0FFFFFFFFFFFFFFF {
|
||||
n += 4
|
||||
x <<= 4
|
||||
}
|
||||
if x <= 0x3FFFFFFFFFFFFFFF {
|
||||
n += 2
|
||||
x <<= 2
|
||||
}
|
||||
if x <= 0x7FFFFFFFFFFFFFFF {
|
||||
n += 1
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// FastCache implements a high-performance cache with minimal locking
|
||||
type FastCache struct {
|
||||
shards []*CacheShard
|
||||
mask uint64
|
||||
}
|
||||
|
||||
// CacheShard represents a single cache shard to reduce lock contention
|
||||
type CacheShard struct {
|
||||
mu sync.RWMutex
|
||||
data map[string]*CacheItem
|
||||
size int
|
||||
limit int
|
||||
}
|
||||
|
||||
// CacheItem represents a cached item with metadata
|
||||
type CacheItem struct {
|
||||
Value interface{}
|
||||
AccessTime int64
|
||||
Cost int
|
||||
}
|
||||
|
||||
// NewFastCache creates a new high-performance cache
|
||||
func NewFastCache(shardCount, itemsPerShard int) *FastCache {
|
||||
// Ensure shard count is power of 2
|
||||
if shardCount&(shardCount-1) != 0 {
|
||||
shardCount = 1 << (32 - countLeadingZeros32(uint32(shardCount-1)))
|
||||
}
|
||||
|
||||
shards := make([]*CacheShard, shardCount)
|
||||
for i := 0; i < shardCount; i++ {
|
||||
shards[i] = &CacheShard{
|
||||
data: make(map[string]*CacheItem, itemsPerShard),
|
||||
limit: itemsPerShard,
|
||||
}
|
||||
}
|
||||
|
||||
return &FastCache{
|
||||
shards: shards,
|
||||
mask: uint64(shardCount - 1),
|
||||
}
|
||||
}
|
||||
|
||||
// countLeadingZeros32 counts leading zeros in a uint32
|
||||
func countLeadingZeros32(x uint32) int {
|
||||
if x == 0 {
|
||||
return 32
|
||||
}
|
||||
n := 0
|
||||
if x <= 0x0000FFFF {
|
||||
n += 16
|
||||
x <<= 16
|
||||
}
|
||||
if x <= 0x00FFFFFF {
|
||||
n += 8
|
||||
x <<= 8
|
||||
}
|
||||
if x <= 0x0FFFFFFF {
|
||||
n += 4
|
||||
x <<= 4
|
||||
}
|
||||
if x <= 0x3FFFFFFF {
|
||||
n += 2
|
||||
x <<= 2
|
||||
}
|
||||
if x <= 0x7FFFFFFF {
|
||||
n += 1
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// hash computes a hash for the key
|
||||
func (c *FastCache) hash(key string) uint64 {
|
||||
hash := uint64(0)
|
||||
for _, b := range key {
|
||||
hash = hash*31 + uint64(b)
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
// getShard returns the shard for a given key
|
||||
func (c *FastCache) getShard(key string) *CacheShard {
|
||||
return c.shards[c.hash(key)&c.mask]
|
||||
}
|
||||
|
||||
// Get retrieves an item from the cache
|
||||
func (c *FastCache) Get(key string) (interface{}, bool) {
|
||||
shard := c.getShard(key)
|
||||
shard.mu.RLock()
|
||||
item, exists := shard.data[key]
|
||||
shard.mu.RUnlock()
|
||||
|
||||
if exists {
|
||||
return item.Value, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Set stores an item in the cache
|
||||
func (c *FastCache) Set(key string, value interface{}, cost int) {
|
||||
shard := c.getShard(key)
|
||||
shard.mu.Lock()
|
||||
|
||||
// Check if we need to evict items
|
||||
if shard.size >= shard.limit && shard.data[key] == nil {
|
||||
c.evictOldest(shard)
|
||||
}
|
||||
|
||||
shard.data[key] = &CacheItem{
|
||||
Value: value,
|
||||
Cost: cost,
|
||||
}
|
||||
shard.size++
|
||||
|
||||
shard.mu.Unlock()
|
||||
}
|
||||
|
||||
// evictOldest removes the oldest item from a shard
|
||||
func (c *FastCache) evictOldest(shard *CacheShard) {
|
||||
var oldestKey string
|
||||
var oldestTime int64 = 1<<63 - 1
|
||||
|
||||
for key, item := range shard.data {
|
||||
if item.AccessTime < oldestTime {
|
||||
oldestTime = item.AccessTime
|
||||
oldestKey = key
|
||||
}
|
||||
}
|
||||
|
||||
if oldestKey != "" {
|
||||
delete(shard.data, oldestKey)
|
||||
shard.size--
|
||||
}
|
||||
}
|
||||
|
||||
// BatchProcessor processes items in batches for better performance
|
||||
type BatchProcessor struct {
|
||||
batchSize int
|
||||
flushTimeout int64 // nanoseconds
|
||||
buffer []interface{}
|
||||
processor func([]interface{}) error
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// NewBatchProcessor creates a new batch processor
|
||||
func NewBatchProcessor(batchSize int, flushTimeoutNs int64, processor func([]interface{}) error) *BatchProcessor {
|
||||
return &BatchProcessor{
|
||||
batchSize: batchSize,
|
||||
flushTimeout: flushTimeoutNs,
|
||||
buffer: make([]interface{}, 0, batchSize),
|
||||
processor: processor,
|
||||
}
|
||||
}
|
||||
|
||||
// Add adds an item to the batch processor
|
||||
func (bp *BatchProcessor) Add(item interface{}) error {
|
||||
bp.mu.Lock()
|
||||
defer bp.mu.Unlock()
|
||||
|
||||
bp.buffer = append(bp.buffer, item)
|
||||
|
||||
if len(bp.buffer) >= bp.batchSize {
|
||||
return bp.flushLocked()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Flush processes all items in the buffer immediately
|
||||
func (bp *BatchProcessor) Flush() error {
|
||||
bp.mu.Lock()
|
||||
defer bp.mu.Unlock()
|
||||
|
||||
return bp.flushLocked()
|
||||
}
|
||||
|
||||
// flushLocked processes items while holding the lock
|
||||
func (bp *BatchProcessor) flushLocked() error {
|
||||
if len(bp.buffer) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
batch := make([]interface{}, len(bp.buffer))
|
||||
copy(batch, bp.buffer)
|
||||
bp.buffer = bp.buffer[:0] // Reset buffer
|
||||
|
||||
return bp.processor(batch)
|
||||
}
|
||||
|
||||
// MemoryOptimizer provides utilities for memory optimization
|
||||
type MemoryOptimizer struct {
|
||||
pools *ObjectPool
|
||||
}
|
||||
|
||||
// NewMemoryOptimizer creates a new memory optimizer
|
||||
func NewMemoryOptimizer() *MemoryOptimizer {
|
||||
return &MemoryOptimizer{
|
||||
pools: NewObjectPool(),
|
||||
}
|
||||
}
|
||||
|
||||
// ProcessWithPools processes data using object pools to minimize allocations
|
||||
func (mo *MemoryOptimizer) ProcessWithPools(data []byte, processor func(*big.Int, *uint256.Int, []byte) error) error {
|
||||
bigInt := mo.pools.GetBigInt()
|
||||
uint256Int := mo.pools.GetUint256()
|
||||
workBuffer := mo.pools.GetByteSlice()
|
||||
|
||||
defer func() {
|
||||
mo.pools.PutBigInt(bigInt)
|
||||
mo.pools.PutUint256(uint256Int)
|
||||
mo.pools.PutByteSlice(workBuffer)
|
||||
}()
|
||||
|
||||
return processor(bigInt, uint256Int, workBuffer)
|
||||
}
|
||||
Reference in New Issue
Block a user