Files
dex/pkg/engine/mlx_engine.go
T

256 lines
6.0 KiB
Go

// Package engine provides the MLX-accelerated matching engine for Mac Studio
// Note: This is a pure Go simulation of MLX acceleration
// In production, this would use CGO bindings to Metal Performance Shaders
package engine
import (
"fmt"
"runtime"
"sync/atomic"
"time"
)
// MLXEngine represents the Metal-accelerated matching engine
type MLXEngine struct {
initialized bool
ordersTotal uint64
tradesTotal uint64
markets uint32
maxMarkets uint32
unifiedMemory bool
gpuCores int
neuralCores int
}
// MLXConfig configures the MLX engine
type MLXConfig struct {
MaxMarkets uint32
MarketDepth uint32
UnifiedMemory bool
CacheGB uint32
BufferGB uint32
}
// NewMLXEngine creates a new MLX-accelerated engine
func NewMLXEngine(config MLXConfig) (*MLXEngine, error) {
// Check if running on Apple Silicon
if runtime.GOOS != "darwin" || runtime.GOARCH != "arm64" {
return nil, fmt.Errorf("MLX engine requires Apple Silicon Mac")
}
engine := &MLXEngine{
initialized: true,
maxMarkets: config.MaxMarkets,
unifiedMemory: config.UnifiedMemory,
gpuCores: detectGPUCores(),
neuralCores: detectNeuralCores(),
}
return engine, nil
}
// ProcessBatch processes a batch of orders on GPU
func (e *MLXEngine) ProcessBatch(orders []Order) (*BatchResult, error) {
if !e.initialized {
return nil, fmt.Errorf("engine not initialized")
}
start := time.Now()
// Simulate MLX GPU processing
// In production, this would use Metal Performance Shaders
processed := uint64(len(orders))
executed := processed / 10 // 10% fill rate
// Simulate 597ns per order latency
processingTime := time.Duration(processed*597) * time.Nanosecond
if processingTime > 0 {
time.Sleep(processingTime / 1000) // Scale down for simulation
}
latency := time.Since(start).Nanoseconds()
throughput := float64(processed) / (float64(latency) / 1e9) / 1e6
// Update totals
atomic.AddUint64(&e.ordersTotal, processed)
atomic.AddUint64(&e.tradesTotal, executed)
return &BatchResult{
OrdersProcessed: processed,
TradesExecuted: executed,
LatencyNanos: uint64(latency),
ThroughputMPS: throughput,
}, nil
}
// GetStats returns engine statistics
func (e *MLXEngine) GetStats() *EngineStats {
return &EngineStats{
OrdersTotal: atomic.LoadUint64(&e.ordersTotal),
TradesTotal: atomic.LoadUint64(&e.tradesTotal),
Markets: e.markets,
GPUCores: e.gpuCores,
NeuralCores: e.neuralCores,
UnifiedMemory: e.unifiedMemory,
}
}
// LoadMarkets loads markets into unified memory
func (e *MLXEngine) LoadMarkets(markets []Market) error {
if uint32(len(markets)) > e.maxMarkets {
return fmt.Errorf("too many markets: %d > %d", len(markets), e.maxMarkets)
}
// Markets are loaded directly into unified memory
// No CPU->GPU transfer needed on Apple Silicon!
e.markets = uint32(len(markets))
return nil
}
// Order represents a trading order
type Order struct {
ID uint64
Symbol string
Price float64
Size float64
Side uint8
Type uint8
}
// Market represents a trading market
type Market struct {
Symbol string
BaseAsset string
QuoteAsset string
TickSize float64
LotSize float64
MaxDepth uint32
}
// BatchResult contains results from batch processing
type BatchResult struct {
OrdersProcessed uint64
TradesExecuted uint64
LatencyNanos uint64
ThroughputMPS float64
}
// EngineStats contains engine statistics
type EngineStats struct {
OrdersTotal uint64
TradesTotal uint64
Markets uint32
GPUCores int
NeuralCores int
UnifiedMemory bool
}
// detectGPUCores detects the number of GPU cores
func detectGPUCores() int {
// M2 Ultra has 76 GPU cores
// M2 Max has 38 GPU cores
// M2 Pro has 19 GPU cores
// M2 has 10 GPU cores
// M1 Ultra has 64 GPU cores
// M1 Max has 32 GPU cores
// M1 Pro has 16 GPU cores
// M1 has 8 GPU cores
// Default to M2 Ultra
return 76
}
// detectNeuralCores detects the number of Neural Engine cores
func detectNeuralCores() int {
// All M2 chips have 16 Neural Engine cores
// M2 Ultra has 32 (2x16)
return 32
}
// hashSymbol creates a fast hash of a symbol
func hashSymbol(symbol string) uint64 {
var hash uint64 = 5381
for _, c := range symbol {
hash = ((hash << 5) + hash) + uint64(c)
}
return hash
}
// Benchmark runs a performance benchmark
func (e *MLXEngine) Benchmark(duration time.Duration) *BenchmarkResult {
// Generate test orders
batchSize := 100000
orders := make([]Order, batchSize)
for i := range orders {
orders[i] = Order{
ID: uint64(i),
Symbol: fmt.Sprintf("TEST%d", i%1000),
Price: 100.0 + float64(i%100),
Size: float64(1 + i%10),
Side: uint8(i % 2),
Type: 0, // Limit
}
}
start := time.Now()
batches := 0
totalOrders := uint64(0)
totalLatency := uint64(0)
for time.Since(start) < duration {
result, err := e.ProcessBatch(orders)
if err != nil {
continue
}
batches++
totalOrders += result.OrdersProcessed
totalLatency += result.LatencyNanos
}
elapsed := time.Since(start)
return &BenchmarkResult{
Duration: elapsed,
OrdersProcessed: totalOrders,
OrdersPerSecond: float64(totalOrders) / elapsed.Seconds(),
AvgLatencyNanos: totalLatency / uint64(batches),
Batches: batches,
}
}
// BenchmarkResult contains benchmark results
type BenchmarkResult struct {
Duration time.Duration
OrdersProcessed uint64
OrdersPerSecond float64
AvgLatencyNanos uint64
Batches int
}
// MemoryInfo returns memory usage information
func (e *MLXEngine) MemoryInfo() *MemoryStats {
var m runtime.MemStats
runtime.ReadMemStats(&m)
return &MemoryStats{
Allocated: m.Alloc,
TotalAllocated: m.TotalAlloc,
System: m.Sys,
NumGC: m.NumGC,
UnifiedTotal: 512 * 1024 * 1024 * 1024, // 512GB
UnifiedUsed: uint64(e.markets) * 160 * 1024, // 160KB per market estimate
}
}
// MemoryStats contains memory statistics
type MemoryStats struct {
Allocated uint64
TotalAllocated uint64
System uint64
NumGC uint32
UnifiedTotal uint64
UnifiedUsed uint64
}