Files
lux/docs/benchmarks.md
Brandon Lucas 2960dd6538 feat: add benchmarks and enhance LSP completions/hover
Benchmarks:
- Add fib, list_ops, primes benchmarks comparing Lux vs Node.js vs Rust
- Lux matches Rust performance and is 8-30x faster than Node.js
- Add docs/benchmarks.md documenting results

LSP improvements:
- Context-aware completions (module access vs general)
- Add List, String, Option, Result, Console, Math method completions
- Add type and builtin completions
- Hover now shows type signatures and documentation for known symbols
- Hover returns formatted markdown with code blocks

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-14 15:23:35 -05:00

3.3 KiB

Lux Performance Benchmarks

This document compares Lux's performance against other languages on common benchmarks.

Benchmark Environment

  • Platform: Linux x86_64
  • Lux: Compiled to native via C backend with -O2 optimization
  • Node.js: v16.x (V8 JIT)
  • Rust: rustc with -O (release optimization)

Results Summary

Benchmark Lux (native) Node.js Rust (native)
Fibonacci(35) 0.013s 0.111s 0.022s
List Ops (10k) 0.001s 0.029s 0.001s
Prime Count (10k) 0.001s 0.031s 0.001s

Key Findings

  1. Lux matches or beats Rust on these benchmarks
  2. Lux is 8-30x faster than Node.js depending on workload
  3. Native compilation pays off - AOT compilation to C produces highly optimized code

Benchmark Details

Fibonacci (Recursive)

Classic recursive Fibonacci calculation - tests function call overhead and recursion.

fn fib(n: Int): Int = {
    if n <= 1 then n
    else fib(n - 1) + fib(n - 2)
}
  • Lux: 0.013s (fastest)
  • Rust: 0.022s
  • Node.js: 0.111s

Lux's C backend generates efficient code with proper tail-call optimization where applicable.

List Operations

Tests functional programming primitives: map, filter, fold on 10,000 elements.

let nums = List.range(1, 10001)
let doubled = List.map(nums, fn(x: Int): Int => x * 2)
let evens = List.filter(doubled, fn(x: Int): Bool => x % 4 == 0)
let sum = List.fold(evens, 0, fn(acc: Int, x: Int): Int => acc + x)
  • Lux: 0.001s
  • Rust: 0.001s
  • Node.js: 0.029s

Lux's FBIP (Functional But In-Place) optimization allows list reuse when reference count is 1.

Prime Counting

Count primes up to 10,000 using trial division - tests loops and conditionals.

fn isPrime(n: Int): Bool = {
    if n < 2 then false
    else if n == 2 then true
    else if n % 2 == 0 then false
    else isPrimeHelper(n, 3)
}
  • Lux: 0.001s
  • Rust: 0.001s
  • Node.js: 0.031s

Why Lux is Fast

1. Native Compilation via C

Lux compiles to C and then to native code using the system C compiler (gcc/clang). This means:

  • Full access to C compiler optimizations (-O2, -O3)
  • No interpreter overhead
  • Direct CPU instruction generation

2. Reference Counting with FBIP

Lux uses Perceus-inspired reference counting with FBIP optimizations:

  • In-place mutation when reference count is 1
  • No garbage collector pauses
  • Predictable memory usage

3. Efficient Function Calls

  • Closures are allocated once and reused
  • Ownership transfer avoids unnecessary reference counting
  • Drop specialization inlines type-specific cleanup

Running Benchmarks

# Run all benchmarks
./benchmarks/run_benchmarks.sh

# Run individual benchmark
cargo run --release -- compile benchmarks/fib.lux -o /tmp/fib && /tmp/fib

Comparison Notes

  • vs Rust: Lux is comparable because both compile to native code with similar optimizations
  • vs Node.js: Lux is much faster because V8's JIT can't match AOT compilation for compute-heavy tasks
  • vs Python: Would be even more dramatic (Python is typically 10-100x slower than Node.js)

Future Improvements

  • Add more benchmarks (sorting, tree operations, string processing)
  • Compare against more languages (Go, Java, OCaml, Haskell)
  • Add memory usage benchmarks
  • Profile and optimize hot paths