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>
3.3 KiB
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
-O2optimization - 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
- Lux matches or beats Rust on these benchmarks
- Lux is 8-30x faster than Node.js depending on workload
- 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