Skip to content

Latest commit

 

History

History
149 lines (114 loc) · 4.42 KB

File metadata and controls

149 lines (114 loc) · 4.42 KB

mtlog Performance Documentation

Benchmark Results

Test Environment

  • CPU: AMD Ryzen 9 9950X 16-Core Processor
  • OS: Windows
  • Go Version: (latest)
  • Date: 2025-07-21

Performance Evolution

Phase 1: Initial Implementation

Basic implementation with no optimizations.

Operation Time/op Bytes/op Allocs/op
Simple log 150.5 ns 288 B 8
Below minimum level 1.4 ns 0 B 0
With 2 properties 412.4 ns 1040 B 21
With 5 properties 858.6 ns 2384 B 39
With context 231.6 ns 576 B 9

Phase 2: Template Caching & Pooling

Added template caching and property map pooling.

Operation Time/op Bytes/op Allocs/op Improvement
Simple log 100.2 ns 128 B 2 33% faster
Below minimum level 1.4 ns 0 B 0 -
With 2 properties 82.5 ns 32 B 1 80% faster
With 5 properties 154.8 ns 80 B 1 82% faster
With context 87.7 ns 0 B 0 62% faster

Phase 3: Zero-Allocation Fast Path

Implemented SimpleSink interface for zero-allocation simple logging.

Operation Time/op Bytes/op Allocs/op Improvement
Simple log 13.6 ns 0 B 0 91% faster
Below minimum level 1.4 ns 0 B 0 -
With 2 properties 182.9 ns 448 B 4 56% faster
With 5 properties 248.4 ns 496 B 4 71% faster
With context 197.1 ns 416 B 3 15% faster

Key Optimizations

  1. Template Caching

    • Templates are parsed once and cached
    • Eliminates repeated parsing overhead
    • Thread-safe with RWMutex
  2. Property Map Pooling

    • Reuses map allocations via sync.Pool
    • Reduces GC pressure
    • Maps cleared and returned to pool
  3. Zero-Allocation Fast Path

    • SimpleSink interface for direct string output
    • Bypasses LogEvent creation for simple messages
    • Conditions: no args, no properties, no enrichers, no filters
  4. Eliminated Double Parsing

    • ExtractPropertyNames works with already-parsed templates
    • Removed redundant parsing in property extraction
  5. Source Context Caching

    • Caches source context by program counter
    • Eliminates repeated runtime stack walking
    • Thread-safe with RWMutex
    • Significant performance improvement for source context enrichment

Comparison with Other Loggers

Logger Simple Log Allocations Notes
mtlog 13.6 ns 0 With fast path
mtlog 100.2 ns 2 Without fast path
zap ~50 ns 0 Requires complex API
zerolog ~50 ns 0 Different API style
logrus ~3000 ns 20+ Feature-rich but slow
stdlib log ~200 ns 2+ Basic functionality

Benchmark Commands

# Run all benchmarks
go test -bench=. -benchmem -run=^$

# Run specific benchmarks
go test -bench=BenchmarkSimpleLog -benchmem -run=^$

# Run with longer duration for stability
go test -bench=. -benchmem -run=^$ -benchtime=10s

# Compare allocations
go test -run=TestAllocationBreakdown -v

Future Optimization Opportunities

  1. String Interning

    • Cache common log messages
    • Reduce string allocations
  2. SIMD Operations

    • Use SIMD for bulk property copying
    • Optimize timestamp formatting
  3. Lock-Free Sinks

    • Implement lock-free ring buffers
    • Reduce contention in high-throughput scenarios
  4. Compile-Time Optimization

    • Generate specialized code for common patterns
    • Use code generation for type-specific paths

Memory Profile

To analyze memory usage:

go test -bench=BenchmarkSimpleLog -memprofile=mem.prof
go tool pprof mem.prof

CPU Profile

To analyze CPU usage:

go test -bench=BenchmarkSimpleLog -cpuprofile=cpu.prof
go tool pprof cpu.prof

Maintaining Performance

When adding new features:

  1. Always run benchmarks before and after changes
  2. Use testing.AllocsPerRun to verify allocation counts
  3. Consider the fast path - will this feature disable it?
  4. Add feature-specific benchmarks
  5. Document any performance trade-offs

Performance Goals

  • Simple logging: < 20ns/op, 0 allocations ✓
  • Structured logging: < 200ns/op, < 5 allocations ✓
  • Below minimum level: < 2ns/op, 0 allocations ✓
  • Memory usage: Minimal heap pressure
  • Concurrency: Scale linearly with cores