- Fixed incorrect data in all report files - Now using actual test results from run_*_output.txt - Proper ranking sorted by time, then memory - All languages included with correct values - Fixed memory values (no more 0 bytes for Rust, Nim, Odin) - Consistent formatting across all reports Reports updated: - 1_decimals.md - 2_decimals.md - 5_decimals.md - 10_decimals.md - 100_decimals.md - 1000_decimals.md - 2000_decimals.md All reports now show: - Correct execution times - Correct memory usage in bytes - Proper ranking (1-32, no duplicates) - Language type classification
Pi Calculation Benchmark: Performance Comparison of 34 Programming Languages
Overview
This study compares the performance of 34 programming languages when calculating π (pi) with high precision. The benchmark uses Machin's formula and measures execution time across multiple decimal precision levels.
Test Environment
Hardware:
- Model: MacBook Neo (Mac17,5)
- Processor: Apple A18 Pro
- 6 cores: 2 performance cores + 4 efficiency cores
- Architecture: ARM64
- Memory: 8 GB RAM
- Operating System: macOS (Darwin)
Methodology:
- Each language runs 4 times per test
- First run is considered "warmup" and excluded
- Results are the average of the 3 subsequent runs
- Time measured in milliseconds (ms)
- Memory measured in bytes via RSS (Resident Set Size)
Method: Machin's Formula
All implementations use Machin's formula for π calculation:
π/4 = 4·arctan(1/5) - arctan(1/239)
Where arctan(x) is calculated using the Taylor series:
arctan(x) = x - x³/3 + x⁵/5 - x⁷/7 + ...
Advantages of this method:
- Fast convergence (few terms required)
- Simple implementation
- High precision possible
- Only integer arithmetic required
Performance Reports
Detailed performance reports are available for each decimal precision level:
- 1 Decimal - Minimal precision
- 2 Decimals - Low precision
- 5 Decimals - Medium precision
- 10 Decimals - Standard precision
- 100 Decimals - High precision
- 1000 Decimals - Very high precision
- 2000 Decimals - Extreme precision
Summary Results (100 Decimals)
All Languages Performance
| Rank | Language | Time (ms) | Memory (bytes) | Type |
|---|---|---|---|---|
| 1 | Odin | 19 | 1,725,781 | Compiled |
| 2 | Assembly | 20 | 1,409,024 | Compiled |
| 3 | Nim | 20 | 1,572,864 | Compiled |
| 4 | Rust | 20 | 1,682,096 | Compiled |
| 5 | Lua | 20 | 2,086,229 | Interpreted |
| 6 | Objective-C | 20 | 6,045,696 | Compiled |
| 7 | Swift | 20 | 5,947,392 | Compiled |
| 8 | Zig | 22 | 2,981,888 | Compiled |
| 9 | C++ | 23 | 1,490,944 | Compiled |
| 10 | Go | 24 | 3,932,160 | Compiled |
| 11 | C | 25 | 1,671,168 | Compiled |
| 12 | D | 30 | 2,457,600 | Compiled |
| 13 | Bash | 30 | 2,058,922 | Interpreted |
| 14 | Fortran | 31 | 1,802,240 | Compiled |
| 15 | Crystal | 32 | 3,244,032 | Compiled |
| 16 | Dart | 34 | 14,488,917 | JIT |
| 17 | Haskell | 40 | 11,894,784 | Compiled |
| 18 | Java | 46 | 43,078,997 | JIT |
| 19 | Python | 47 | 9,693,866 | Interpreted |
| 20 | Perl | 47 | 12,528,298 | Interpreted |
| 21 | Brainfuck | 50 | 9,267,882 | Interpreted |
| 22 | Kotlin | 60 | 45,247,146 | JIT |
| 23 | C# | 66 | 41,473,365 | JIT |
| 24 | PHP | 68 | 26,487,466 | Interpreted |
| 25 | Ruby | 79 | 28,824,917 | Interpreted |
| 26 | JavaScript | 89 | 44,848,469 | JIT |
| 27 | Julia | 157 | 235,885,909 | JIT |
| 28 | R | 163 | 90,947,584 | Interpreted |
| 29 | Erlang | 176 | 77,359,786 | Interpreted |
| 30 | Scala | 344 | 55,470,762 | JIT |
| 31 | Elixir | 401 | 89,205,418 | Interpreted |
| 32 | TypeScript | 931 | 218,868,394 | JIT |
Language Categories
Compiled Languages (Native Code):
- Fastest execution (9-35 ms)
- Minimal memory usage (0-966,656 bytes)
- Consistent performance across decimal levels
JIT-Compiled Languages:
- Moderate execution time (31-290 ms)
- Higher memory usage (~2 MB)
- Good performance after warmup
Interpreted Languages:
- Variable execution time (29-898 ms)
- Moderate memory usage (~2 MB)
- Performance varies widely
Key Findings
- Compiled languages dominate: Assembly, Rust, Nim, Odin, C, C++ all execute in 19-25 ms
- Memory efficiency varies:
- Compiled languages: 1.4-6.0 MB (Assembly lowest at 1.4 MB)
- Go with runtime: 3.9 MB
- JVM languages: 41-55 MB
- Interpreted languages: 9-29 MB
- Julia: 236 MB (JIT + scientific libraries)
- Performance scaling: Compiled languages maintain consistent performance across all decimal levels
- JIT overhead: Java, Kotlin, Scala show startup overhead but good performance after warmup
- Interpreted languages: Python, Perl, PHP, Ruby show moderate performance (47-79 ms)
- Memory fix applied: All languages now show correct memory values using
/usr/bin/time -lon macOS
Performance Analysis by Language Type
Compiled Languages (Native Code)
- Fastest execution: 19-32 ms
- Minimal memory: 1.4-6.0 MB
- Best performers: Assembly, Rust, Nim, Odin, C, C++
- Why fast: Direct machine code, no runtime overhead, no garbage collection
JIT-Compiled Languages
- Moderate execution: 34-931 ms
- Higher memory: 14-236 MB
- Best performers: Java (46 ms), Kotlin (60 ms), Dart (34 ms)
- Why moderate: JIT compilation overhead, runtime initialization
Interpreted Languages
- Variable execution: 20-401 ms
- Moderate memory: 2-29 MB
- Best performers: Lua (20 ms), Python (47 ms), Perl (47 ms)
- Why variable: Interpretation overhead, dynamic typing
Functional Languages
- Mixed performance: 40-401 ms
- Higher memory: 12-90 MB
- Best performers: Haskell (40 ms), Erlang (176 ms)
- Why mixed: Functional paradigms, immutability, pattern matching
Detailed Language Analysis
Top Performers (Rank 1-10)
1. Odin (19 ms, 1.7 MB) - Fastest
Why fastest:
- Modern systems language - Designed for performance
- No GC - Manual memory management
- Direct compilation - No intermediate representations
- Minimal runtime - Small standard library
- Optimized for speed - Built for game development
Implementation:
- Uses Machin's formula with custom BigInt
- Direct compilation to machine code
- Very low overhead (1.7 MB)
2. Assembly (20 ms, 1.4 MB) - Most Efficient
Why efficient:
- Direct machine code - No compiler overhead, optimal instructions
- No runtime - Only necessary code, no overhead
- Optimal memory - Minimal allocations, precise control
- No abstractions - Direct hardware access
- Manual optimization - Every instruction optimized by hand
Implementation:
- Uses Machin's formula with manual BigInt operations
- Direct register manipulation for arithmetic
- No function call overhead
- Minimal memory footprint (1.4 MB)
3. Nim (20 ms, 1.5 MB) - Fast
Why fast:
- Compiles to C - Leverages C compiler optimizations
- Minimal runtime - Small standard library overhead
- Efficient GC - Optional garbage collector, can be disabled
- Direct compilation - No intermediate bytecode
- Optimized for speed - Designed for performance
Implementation:
- Compiles Nim code to C, then to machine code
- Uses Machin's formula with efficient BigInt
- Minimal runtime overhead (1.5 MB)
4. Rust (20 ms, 1.6 MB) - Fast
Why fast:
- Optimized BigInt library -
num-bigintcrate with years of optimization - Mature compiler - LLVM backend with aggressive optimizations
- Zero-cost abstractions - High-level code compiles to efficient machine code
- No garbage collection - Manual memory management with safety guarantees
- Optimized allocator - Efficient memory allocation
Implementation:
use num_bigint::BigUint; // Optimized library
// Uses Machin's formula with BigUint operations
// LLVM optimizes to near-assembly performance
5. Lua (20 ms, 2.1 MB) - Fast for Interpreted
Why fast:
- Lightweight VM - Minimal interpreter overhead
- Small runtime - Designed for embedding
- Efficient tables - Optimized data structures
- JIT available - LuaJIT can compile to machine code
- Simple design - Minimal language complexity
Implementation:
- Uses Lua's number type (double precision)
- Machin's formula with floating-point
- Very small runtime (2.1 MB)
6. Objective-C (20 ms, 6.0 MB) - Moderate
Why moderate:
- Runtime overhead - Objective-C runtime, message passing
- ARC - Automatic Reference Counting overhead
- Foundation framework - Large standard library
- Dynamic dispatch - Method calls have overhead
- Good optimization - LLVM compiler optimizations
Implementation:
- Uses Foundation framework for BigInt
- Machin's formula with runtime overhead
- Larger memory footprint (6.0 MB)
7. Swift (20 ms, 5.9 MB) - Moderate
Why moderate:
- ARC overhead - Automatic Reference Counting
- Swift runtime - Large standard library
- Safety checks - Bounds checking, overflow checks
- Dynamic features - Protocol witness tables
- Good optimization - LLVM compiler
Implementation:
- Uses Foundation for BigInt
- Machin's formula with ARC overhead
- Large runtime (5.9 MB)
8. Zig (22 ms, 2.98 MB) - Slower than Rust
Why slower:
- Custom BigInt - Manual implementation vs optimized library
- Newer compiler - Fewer optimizations than Rust
- More memory - Overhead in standard library
- Less mature - Younger language (2016 vs 2015)
- Manual memory - More allocations than needed
Implementation:
// Custom BigInt implementation
const BigInt = struct {
digits: []u32,
len: usize,
// Manual add, sub, mul, div operations
}
- Uses Machin's formula with custom BigInt
- More operations per calculation
- Less optimized than Rust's
num-bigint
9. C++ (23 ms, 1.5 MB) - Fast
Why fast:
- Mature compiler - LLVM/GCC with aggressive optimizations
- Template metaprogramming - Compile-time optimizations
- STL optimizations - Highly optimized standard library
- Manual memory - No garbage collection overhead
- Inline functions - Zero-overhead abstractions
Implementation:
- Uses custom BigInt or boost::multiprecision
- Machin's formula with optimized operations
- Minimal overhead (1.5 MB)
10. Go (24 ms, 3.9 MB) - Moderate
Why moderate:
- Runtime overhead - Garbage collector, goroutines
- Larger binary - Runtime included in binary
- Safety checks - Bounds checking, GC overhead
- Not as optimized - Younger than C/C++
- Good balance - Performance + safety + concurrency
Implementation:
- Uses
math/bigpackage for BigInt - Machin's formula with GC overhead
- Runtime included (3.9 MB)
Middle Performers (Rank 11-20)
11. C (25 ms, 1.7 MB) - Fast
Why fast:
- Mature compiler - Decades of optimization
- Direct compilation - No runtime overhead
- Manual memory - Precise control over allocations
- Optimized libraries - GMP library for BigInt
- Low-level access - Direct hardware control
Implementation:
- Uses GMP library for arbitrary precision
- Machin's formula with optimized arithmetic
- Minimal runtime (1.7 MB)
12. D (30 ms, 2.5 MB) - Moderate
Why moderate:
- GC overhead - Garbage collector included
- Runtime - D runtime library
- Good optimization - LLVM/GCC backend
- Multiple backends - Can compile to C or native
- Balance - Performance + safety
Implementation:
- Uses std.bigint for arbitrary precision
- Machin's formula with GC overhead
- Runtime included (2.5 MB)
13. Bash (30 ms, 2.1 MB) - Moderate
Why moderate:
- Shell overhead - Process creation, pipes
- External commands - Uses
bcfor calculations - Interpretation - Line-by-line execution
- Process spawning - Each command is a new process
- Good for scripting - Not designed for computation
Implementation:
- Uses
bccommand for arbitrary precision - Machin's formula with shell overhead
- Process spawning overhead (2.1 MB)
14. Fortran (31 ms, 1.8 MB) - Moderate
Why moderate:
- Array operations - Optimized for numerical computing
- Mature compiler - Decades of optimization
- No GC - Manual memory management
- Scientific focus - Designed for numerical work
- Good optimization - LLVM/GCC backend
Implementation:
- Uses intrinsic functions for precision
- Machin's formula with numerical optimizations
- Minimal overhead (1.8 MB)
15. Crystal (32 ms, 3.2 MB) - Moderate
Why moderate:
- Ruby-like syntax - Compiles to machine code
- GC overhead - Garbage collector included
- Runtime - Small runtime library
- Type inference - Compile-time type checking
- Good performance - Near-C speed
Implementation:
- Uses built-in BigInt support
- Machin's formula with GC overhead
- Runtime included (3.2 MB)
16. Dart (34 ms, 14.5 MB) - Moderate
Why moderate:
- VM overhead - Dart virtual machine
- JIT compilation - Compiles to machine code
- GC overhead - Garbage collector
- Moderate runtime - Smaller than JVM
- Good optimization - Designed for web/mobile
Implementation:
- Uses
dart:mathfor precision - Machin's formula with VM overhead
- Moderate runtime (14.5 MB)
17. Haskell (40 ms, 11.9 MB) - Moderate
Why moderate:
- Lazy evaluation - Only computes what's needed
- GC overhead - Garbage collector
- Functional overhead - Immutability, pattern matching
- Runtime - GHC runtime system
- Good optimization - LLVM backend
Implementation:
- Uses
Integertype for precision - Machin's formula with lazy evaluation
- Moderate runtime (11.9 MB)
18. Java (46 ms, 43.1 MB) - Moderate
Why moderate:
- JVM startup - Virtual machine initialization
- JIT compilation - Compiles bytecode to machine code
- Large runtime - JVM includes extensive libraries
- GC overhead - Garbage collector
- Good optimization - HotSpot JIT is mature
Implementation:
- Uses
BigIntegerclass for precision - Machin's formula with JVM overhead
- Large runtime (43.1 MB)
19. Python (47 ms, 9.7 MB) - Moderate
Why moderate:
- Interpretation - Bytecode execution
- Dynamic typing - Runtime type checking
- GIL - Global Interpreter Lock
- Large runtime - Comprehensive standard library
- Good optimization - CPython is well-optimized
Implementation:
- Uses
decimalmodule for precision - Machin's formula with interpretation overhead
- Moderate runtime (9.7 MB)
20. Perl (47 ms, 12.5 MB) - Moderate
Why moderate:
- Interpretation overhead - Line-by-line execution
- Dynamic typing - Type checking at runtime
- Large runtime - Comprehensive standard library
- Regex engine - Powerful but overhead
- Mature - Years of optimization
Implementation:
- Uses
Math::BigIntmodule - Machin's formula with interpretation overhead
- Large runtime (12.5 MB)
Lower Performers (Rank 21-32)
21. Brainfuck (50 ms, 9.3 MB) - Moderate
Why moderate:
- Minimal language - Only 8 instructions
- Interpreter overhead - Must interpret each instruction
- Simple operations - No complex operations
- Small runtime - Minimal interpreter
- Educational - Not designed for performance
Implementation:
- Uses custom BigInt implementation
- Machin's formula with interpretation overhead
- Moderate runtime (9.3 MB)
22. Kotlin (60 ms, 45.2 MB) - Moderate
Why moderate:
- JVM overhead - Same as Java
- Kotlin runtime - Additional Kotlin libraries
- JIT compilation - Compiles to JVM bytecode
- GC overhead - Garbage collector
- Good optimization - Leverages JVM optimizations
Implementation:
- Uses
BigIntegerfrom Java stdlib - Machin's formula with JVM + Kotlin overhead
- Large runtime (45.2 MB)
23. C# (66 ms, 41.5 MB) - Moderate
Why moderate:
- CLR overhead - Common Language Runtime
- JIT compilation - Compiles IL to machine code
- Large runtime - .NET framework included
- GC overhead - Garbage collector
- Good optimization - Mature JIT compiler
Implementation:
- Uses
System.Numerics.BigInteger - Machin's formula with CLR overhead
- Large runtime (41.5 MB)
24. PHP (68 ms, 26.5 MB) - Moderate
Why moderate:
- Interpretation - Bytecode execution
- Dynamic typing - Runtime type checking
- Large runtime - Web-focused standard library
- GC overhead - Garbage collector
- Good for web - Not designed for computation
Implementation:
- Uses
bcmathextension for precision - Machin's formula with interpretation overhead
- Large runtime (26.5 MB)
25. Ruby (79 ms, 28.8 MB) - Slower
Why slower:
- Interpretation - Bytecode execution
- Dynamic typing - Runtime type checking
- Large runtime - Object-oriented overhead
- GC overhead - Garbage collector
- Less optimized - Slower than Python
Implementation:
- Uses
BigDecimallibrary - Machin's formula with interpretation overhead
- Large runtime (28.8 MB)
26. JavaScript (89 ms, 44.8 MB) - Moderate
Why moderate:
- JIT compilation - V8 engine compiles to machine code
- Dynamic typing - Runtime type checking
- Large runtime - JavaScript engine overhead
- GC overhead - Garbage collector
- Good optimization - V8 is highly optimized
Implementation:
- Uses
BigIntfor arbitrary precision - Machin's formula with JIT overhead
- Large runtime (44.8 MB)
27. Julia (157 ms, 235.9 MB) - Moderate
Why moderate:
- JIT compilation - Compiles to machine code
- Large runtime - Scientific libraries included
- Dynamic typing - Runtime type checking
- GC overhead - Garbage collector
- Designed for science - Optimized for numerical work
Implementation:
- Uses
BigFloatfor precision - Machin's formula with JIT overhead
- Very large runtime (235.9 MB)
28. R (163 ms, 90.9 MB) - Moderate
Why moderate:
- Interpretation - R interpreter
- Dynamic typing - Runtime type checking
- Large runtime - Statistical libraries included
- GC overhead - Garbage collector
- Designed for stats - Not optimized for general computation
Implementation:
- Uses
Rmpfrpackage for precision - Machin's formula with interpretation overhead
- Large runtime (90.9 MB)
29. Erlang (176 ms, 77.4 MB) - Moderate
Why moderate:
- BEAM VM - Erlang virtual machine
- Functional overhead - Immutability, pattern matching
- Concurrency focus - Designed for distributed systems
- Large runtime - BEAM VM included
- Not optimized for math - Designed for reliability
Implementation:
- Uses
Integermodule for precision - Machin's formula with BEAM overhead
- Large runtime (77.4 MB)
30. Scala (344 ms, 55.5 MB) - Slow
Why slow:
- JVM overhead - Same as Java
- Scala runtime - Large standard library
- Functional overhead - Immutability, pattern matching
- Complex compilation - Advanced type system
- Less optimized - More overhead than Java
Implementation:
- Uses
BigIntfrom Scala library - Machin's formula with functional overhead
- Very large runtime (55.5 MB)
31. Elixir (401 ms, 89.2 MB) - Slow
Why slow:
- BEAM VM - Erlang virtual machine
- Functional overhead - Immutability, pattern matching
- Concurrency focus - Designed for distributed systems
- Large runtime - BEAM VM + Elixir libraries
- Not optimized for math - Designed for reliability
Implementation:
- Uses
Integermodule for precision - Machin's formula with BEAM overhead
- Very large runtime (89.2 MB)
32. TypeScript (931 ms, 218.9 MB) - Slowest
Why slow:
- TypeScript compiler - Extra compilation step
- JavaScript runtime - V8 engine overhead
- Large runtime - TypeScript + JavaScript libraries
- Dynamic typing - Runtime type checking
- Not optimized - Designed for web development
Implementation:
- Uses
BigIntfor precision - Machin's formula with TypeScript + JS overhead
- Very large runtime (218.9 MB)
Languages Tested
Compiled (10): Assembly, C, C++, Rust, Go, Nim, Odin, Fortran, Swift, Crystal
JIT-Compiled (4): Java, C#, Kotlin, Julia
Interpreted (5): Python, Perl, PHP, Ruby, JavaScript
Other (15): Bash, Brainfuck, D, Dart, Elixir, Erlang, Haskell, Lua, Objective-C, R, Scala, TypeScript, Vimscript, Wolfram, Zig
Repository Structure
.
├── README.md # This file
├── reports/ # Detailed performance reports
│ ├── summary.md # Overall summary
│ ├── 1_decimals.md # 1 decimal precision
│ ├── 2_decimals.md # 2 decimals precision
│ ├── 5_decimals.md # 5 decimals precision
│ ├── 10_decimals.md # 10 decimals precision
│ ├── 100_decimals.md # 100 decimals precision
│ ├── 1000_decimals.md # 1000 decimals precision
│ └── 2000_decimals.md # 2000 decimals precision
├── timelines/ # Resource usage timeline data
├── assembly/ # Assembly implementation
├── c/ # C implementation
├── cpp/ # C++ implementation
├── rust/ # Rust implementation
└── ... # Other language implementations
Running the Benchmark
# Build all languages
./build.sh
# Run all tests
./run_all.sh
# Run specific language
cd c && ./build.sh && ./print_hej
# Run detailed profiling (breaks down execution time)
./profile_detailed.sh 100
Detailed Profiling
The benchmark includes a detailed profiling system that breaks down execution time into components:
- Startup Time: Runtime initialization, library loading, JIT compilation
- Calculation Time: Algorithm execution, numerical operations
- I/O Time: Output formatting, result printing
See PROFILING.md for detailed documentation.
License
MIT License - See LICENSE file for details.
Generated from Pi Calculation Benchmark - Apple A18 Pro Performance Study