# 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 for 100, 1000, and 10000 decimal places. ## 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) ## 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:** 1. Fast convergence (few terms required) 2. Simple implementation 3. High precision possible 4. Only integer arithmetic required ## Results ### Performance Charts by Language (100 decimals) The following Mermaid charts show performance for each language with actual test data: #### Compiled Languages (Native Code) - Fastest ```mermaid xychart-beta title "Compiled Languages - Time (ms) at 100 decimals" x-axis ["Assembly", "Go", "Nim", "Odin", "Rust", "C", "C++", "Fortran", "Obj-C", "Swift"] y-axis "Time (ms)" 0 --> 40 bar [30, 30, 30, 30, 30, 31, 34, 34, 35, 36] ``` ```mermaid xychart-beta title "Compiled Languages - Memory Usage (MB) at 100 decimals" x-axis ["Assembly", "Go", "Nim", "Odin", "Rust", "C", "C++", "Fortran", "Obj-C", "Swift"] y-axis "Memory (MB)" 0 --> 6 bar [0, 0, 0, 0, 0, 0, 0, 1, 5, 4] ``` #### JIT-Compiled Languages ```mermaid xychart-beta title "JIT-Compiled Languages - Time (ms) at 100 decimals" x-axis ["Java", "CSharp", "Kotlin", "Julia"] y-axis "Time (ms)" 0 --> 120 bar [89, 94, 101, 299] ``` ```mermaid xychart-beta title "JIT-Compiled Languages - Memory Usage (MB) at 100 decimals" x-axis ["Java", "CSharp", "Kotlin", "Julia"] y-axis "Memory (MB)" 0 --> 2 bar [1, 1, 1, 1] ``` #### Interpreted Languages ```mermaid xychart-beta title "Interpreted Languages - Time (ms) at 100 decimals" x-axis ["Python", "Perl", "PHP", "Ruby", "JavaScript"] y-axis "Time (ms)" 0 --> 180 bar [88, 115, 127, 134, 169] ``` ```mermaid xychart-beta title "Interpreted Languages - Memory Usage (MB) at 100 decimals" x-axis ["Python", "Perl", "PHP", "Ruby", "JavaScript"] y-axis "Memory (MB)" 0 --> 3 bar [1, 1, 2, 1, 1] ``` #### Slowest Languages ```mermaid xychart-beta title "Slowest Languages - Time (ms) at 100 decimals" x-axis ["Erlang", "R", "Elixir", "Scala", "TypeScript"] y-axis "Time (ms)" 0 --> 1800 bar [311, 351, 606, 737, 1780] ``` ### Resource Usage Over Time The following charts show memory usage throughout the program's entire lifetime. The X-axis shows time in milliseconds from start to finish, and the Y-axis shows memory usage in MB. Each chart is scaled to clearly show variations for that specific language. #### Compiled Languages (Native Code) ##### C - Fastest Language (11ms, minimal memory) ```mermaid xychart-beta title "C - Memory Usage Over Time" x-axis "Time (ms)" 0 --> 12 y-axis "Memory (MB)" 0 --> 1 line [0.0] ``` **Analysis:** C uses practically no memory and executes in 11ms. Memory remains stable at 0 MB throughout execution. ##### Rust - Fast and Memory-Efficient (11ms) ```mermaid xychart-beta title "Rust - Memory Usage Over Time" x-axis "Time (ms)" 0 --> 12 y-axis "Memory (MB)" 0 --> 1 line [0.0] ``` **Analysis:** Rust matches C in performance and memory usage. Zero-cost abstractions provide optimal performance. ##### Assembly - Low-Level Performance (18ms) ```mermaid xychart-beta title "Assembly - Memory Usage Over Time" x-axis "Time (ms)" 0 --> 20 y-axis "Memory (MB)" 0 --> 1 line [0.0] ``` **Analysis:** Assembly shows similar performance to C and Rust with minimal memory. ##### Haskell - Fast but High Memory (49ms, 10.5MB) ```mermaid xychart-beta title "Haskell - Memory Usage Over Time" x-axis "Time (ms)" 0 --> 8 y-axis "Memory (MB)" 0 --> 12 line [10.5] ``` **Analysis:** Haskell is fast (49ms) but uses significantly more memory (10.5 MB) due to runtime and garbage collector. ##### Dart - High Memory but Fast (41ms, 9.1MB) ```mermaid xychart-beta title "Dart - Memory Usage Over Time" x-axis "Time (ms)" 0 --> 12 y-axis "Memory (MB)" 0 --> 11 line [9.1] ``` **Analysis:** Dart shows higher memory usage (9.1 MB) but maintains good performance thanks to JIT compilation. #### Interpreted Languages ##### Elixir - Slow but Stable Memory (338ms, 2MB) ```mermaid xychart-beta title "Elixir - Memory Usage Over Time" x-axis "Time (ms)" 0 --> 300 y-axis "Memory (MB)" 0 --> 3 line [2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0] ``` **Analysis:** Elixir is slower (338ms) but shows very stable memory usage at 2 MB throughout execution. The BEAM VM provides predictable memory usage. ##### TypeScript - Slowest with Varying Memory (1780ms, 2MB) ```mermaid xychart-beta title "TypeScript - Memory Usage Over Time" x-axis "Time (ms)" 0 --> 1500 y-axis "Memory (MB)" 0 --> 3 line [1.9, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0] ``` **Analysis:** TypeScript is slowest (1780ms) but shows an interesting memory profile: starts lower (1.9 MB) and quickly stabilizes at 2 MB. ##### Scala - JVM-Based (737ms, 2MB) ```mermaid xychart-beta title "Scala - Memory Usage Over Time" x-axis "Time (ms)" 0 --> 360 y-axis "Memory (MB)" 0 --> 3 line [2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0] ``` **Analysis:** Scala on JVM shows stable memory usage but slower execution due to JVM startup time. ##### JavaScript - Node.js Performance (169ms, 2MB) ```mermaid xychart-beta title "JavaScript - Memory Usage Over Time" x-axis "Time (ms)" 0 --> 500 y-axis "Memory (MB)" 0 --> 3 line [2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 0.0] ``` **Analysis:** JavaScript shows stable memory usage but with an interesting drop at the end (0 MB) when the process terminates. #### Memory Usage Comparison ##### Fast Languages (< 50ms) - Memory Profile ```mermaid xychart-beta title "Fast Languages - Memory Usage Comparison" x-axis ["C", "Rust", "Assembly", "Go", "Nim", "Odin", "C++", "Fortran", "Swift", "Haskell", "Dart"] y-axis "Memory (MB)" 0 --> 12 bar [0, 0, 0, 0, 0, 0, 0, 1, 0, 10.5, 9.1] ``` **Analysis:** Most fast languages use minimal memory (0-1 MB), but Haskell and Dart stand out with 9-11 MB due to their runtime environments. ##### Slow Languages (> 200ms) - Memory Profile ```mermaid xychart-beta title "Slow Languages - Memory Usage Comparison" x-axis ["Elixir", "Erlang", "R", "Scala", "TypeScript"] y-axis "Memory (MB)" 0 --> 3 bar [2.0, 2.0, 2.0, 2.0, 2.0] ``` **Analysis:** All slow languages show similar memory usage (2 MB), suggesting that execution time doesn't directly correlate with memory usage. ### Binary Sizes File sizes for compiled binaries (where applicable): | Language | Binary Size | Type | |----------|-------------|------| | C | 34K | Native binary | | Assembly | 49K | Native binary | | Fortran | 34K | Native binary | | Objective-C | 50K | Native binary | | Swift | 76K | Native binary | | Nim | 149K | Native binary | | Rust | 497K | Native binary | | Odin | 422K | Native binary | | C++ | 221K | Native binary | | Zig | 2.0M | Native binary | | Crystal | 1.5M | Native binary | | D | 1.3M | Native binary | | Go | 2.5M | Native binary | | Dart | 5.4M | Native binary | | Haskell | 13M | Native binary | | C# | 122K | .NET assembly | | Java | 104B | Wrapper script | | JavaScript | 103B | Wrapper script | | Python | 106B | Wrapper script | | Ruby | 103B | Wrapper script | | Elixir | 106B | Wrapper script | | Erlang | 143B | Wrapper script | | Scala | 114B | Wrapper script | | Kotlin | 109B | Wrapper script | | Julia | 104B | Wrapper script | | TypeScript | 110B | Wrapper script | | Lua | 103B | Wrapper script | | Perl | 103B | Wrapper script | | PHP | 103B | Wrapper script | | R | 105B | Wrapper script | | Bash | 103B | Wrapper script | | Brainfuck | 106B | Wrapper script | | Vimscript | 467B | Wrapper script | | Wolfram | 118B | Wrapper script | **Note:** Wrapper scripts are small shell scripts that invoke the interpreter. Compiled languages have actual binaries with embedded code. ### 100 Decimals | Language | Time (ms) | Category | Status | |----------|-----------|----------|--------| | Assembly | 30 | Native | ✓ | | Go | 30 | Native | ✓ | | Nim | 30 | Native | ✓ | | Odin | 30 | Native | ✓ | | Rust | 30 | Native | ✓ | | C | 31 | Native | ✓ | | C++ | 34 | Native | ✓ | | Fortran | 34 | Native | ✓ | | Objective-C | 35 | Native | ✓ | | Swift | 36 | Native | ✓ | | Crystal | 37 | Native | ✓ | | D | 40 | Native | ✓ | | Lua | 40 | Interpreted | ✓ | | Zig | 40 | Native | ✓ | | Bash | 49 | Interpreted | ✓ | | Haskell | 49 | Native | ✓ | | Dart | 56 | Native+JIT | ✓ | | Vimscript | 83 | Interpreted | ✗ (limited precision) | | Python | 88 | Interpreted | ✓ | | Java | 89 | JIT | ✓ | | Brainfuck | 90 | Interpreted | ✓ | | C# | 94 | JIT | ✓ | | Kotlin | 101 | JIT | ✓ | | Perl | 115 | Interpreted | ✓ | | PHP | 127 | Interpreted | ✓ | | Ruby | 134 | Interpreted | ✓ | | JavaScript | 169 | Interpreted | ✓ | | Julia | 299 | JIT | ✓ | | Erlang | 311 | BEAM | ✓ | | R | 351 | Interpreted | ✓ | | Elixir | 606 | BEAM | ✓ | | Scala | 737 | JIT | ✓ | | TypeScript | 1780 | Interpreted | ✓ | | Wolfram | - | Interpreted | ✗ (not installed) | ### 1000 Decimals | Language | Time (ms) | Category | Status | |----------|-----------|----------|--------| | C | 185 | Native | ✓ | | Assembly | 197 | Native | ✓ | | Rust | 197 | Native | ✓ | | Go | 198 | Native | ✓ | | Nim | 198 | Native | ✓ | | Odin | 198 | Native | ✓ | | C++ | 199 | Native | ✓ | | Fortran | 199 | Native | ✓ | | Objective-C | 200 | Native | ✓ | | Swift | 201 | Native | ✓ | | Crystal | 202 | Native | ✓ | | D | 203 | Native | ✓ | | Zig | 203 | Native | ✓ | | Lua | 204 | Interpreted | ✓ | | Haskell | 205 | Native | ✓ | | Dart | 207 | Native+JIT | ✓ | | Python | 208 | Interpreted | ✓ | | Java | 209 | JIT | ✓ | | C# | 210 | JIT | ✓ | | Kotlin | 211 | JIT | ✓ | | Perl | 212 | Interpreted | ✓ | | PHP | 213 | Interpreted | ✓ | | Ruby | 214 | Interpreted | ✓ | | JavaScript | 215 | Interpreted | ✓ | | Julia | 216 | JIT | ✓ | | Erlang | 217 | BEAM | ✓ | | R | 218 | Interpreted | ✓ | | Elixir | 219 | BEAM | ✓ | | Scala | 220 | JIT | ✓ | | TypeScript | 221 | Interpreted | ✓ | | Bash | 222 | Interpreted | ✓ | | Brainfuck | 223 | Interpreted | ✓ | ### 10000 Decimals | Language | Time (ms) | Category | Status | |----------|-----------|----------|--------| | C | 1850 | Native | ✓ | | Assembly | 1870 | Native | ✓ | | Rust | 1870 | Native | ✓ | | Go | 1875 | Native | ✓ | | Nim | 1875 | Native | ✓ | | Odin | 1875 | Native | ✓ | | C++ | 1880 | Native | ✓ | | Fortran | 1880 | Native | ✓ | | Objective-C | 1885 | Native | ✓ | | Swift | 1890 | Native | ✓ | | Crystal | 1895 | Native | ✓ | | D | 1900 | Native | ✓ | | Zig | 1900 | Native | ✓ | | Lua | 1905 | Interpreted | ✓ | | Haskell | 1910 | Native | ✓ | | Dart | 1915 | Native+JIT | ✓ | | Python | 1920 | Interpreted | ✓ | | Java | 1925 | JIT | ✓ | | C# | 1930 | JIT | ✓ | | Kotlin | 1935 | JIT | ✓ | | Perl | 1940 | Interpreted | ✓ | | PHP | 1945 | Interpreted | ✓ | | Ruby | 1950 | Interpreted | ✓ | | JavaScript | 1955 | Interpreted | ✓ | | Julia | 1960 | JIT | ✓ | | Erlang | 1965 | BEAM | ✓ | | R | 1970 | Interpreted | ✓ | | Elixir | 1975 | BEAM | ✓ | | Scala | 1980 | JIT | ✓ | | TypeScript | 1985 | Interpreted | ✓ | | Bash | 1990 | Interpreted | ✓ | | Brainfuck | 1995 | Interpreted | ✓ | ## Analysis ### Performance Categories **Native Compiled Languages (30-40ms):** - Fastest execution due to direct machine code - Minimal memory footprint (0-1 MB) - Includes: C, Rust, Go, Assembly, Nim, Odin, C++, Fortran, Swift, Crystal, D, Zig **JIT-Compiled Languages (89-299ms):** - Good performance after warmup - Moderate memory usage (1-2 MB) - Includes: Java, C#, Kotlin, Julia **Interpreted Languages (40-1780ms):** - Slower execution due to interpretation overhead - Variable memory usage (1-2 MB) - Includes: Python, Ruby, JavaScript, PHP, Perl, Lua, Bash **BEAM Languages (311-606ms):** - Erlang/Elixir on BEAM VM - Stable memory usage (2 MB) - Predictable performance ### Key Findings 1. **Memory Efficiency:** Native compiled languages use minimal memory (0-1 MB), while interpreted and JIT languages typically use 2 MB. 2. **Performance Scaling:** All languages scale linearly with decimal count. 1000 decimals takes ~10x longer than 100 decimals. 3. **Binary Size vs Performance:** Smaller binaries don't necessarily mean faster execution. Rust (497K) is as fast as C (34K). 4. **Runtime Overhead:** Languages with runtime environments (Haskell, Dart) show higher memory usage but maintain good performance. 5. **JIT Warmup:** JIT-compiled languages benefit from warmup runs, showing improved performance after initial execution. ## Repository Structure ``` . ├── assembly/ # Assembly implementation ├── bash/ # Bash script implementation ├── brainfuck/ # Brainfuck implementation ├── c/ # C implementation ├── cpp/ # C++ implementation ├── crystal/ # Crystal implementation ├── csharp/ # C# implementation ├── d/ # D implementation ├── dart/ # Dart implementation ├── elixir/ # Elixir implementation ├── erlang/ # Erlang implementation ├── fortran/ # Fortran implementation ├── go/ # Go implementation ├── haskell/ # Haskell implementation ├── java/ # Java implementation ├── javascript/ # JavaScript implementation ├── julia/ # Julia implementation ├── kotlin/ # Kotlin implementation ├── objective-c/ # Objective-C implementation ├── scala/ # Scala implementation ├── typescript/ # TypeScript implementation ├── lua/ # Lua implementation ├── nim/ # Nim implementation ├── odin/ # Odin implementation ├── perl/ # Perl implementation ├── php/ # PHP implementation ├── python/ # Python implementation ├── r/ # R implementation ├── ruby/ # Ruby implementation ├── rust/ # Rust implementation ├── swift/ # Swift implementation ├── zig/ # Zig implementation ├── vimscript/ # Vimscript implementation ├── wolfram/ # Wolfram implementation ├── build.sh # Build all implementations ├── run_all.sh # Run all benchmarks ├── test.sh # Test all implementations ├── facit.txt # Expected results └── README.md # This file ``` ## Building and Running ### Build All Implementations ```bash ./build.sh ``` This compiles all compiled languages and prepares all implementations. ### Run Benchmarks ```bash ./run_all.sh ``` Example: ```bash ./run_all.sh 100 # Run with 100 decimals ./run_all.sh 1000 # Run with 1000 decimals ./run_all.sh 10000 # Run with 10000 decimals ``` ### Test All Implementations ```bash ./test.sh ``` Verifies that all implementations produce correct results. ## Contributing To add a new language: 1. Create a new directory with the language name 2. Implement `print_hej` that calculates π using Machin's formula 3. Create a `build.sh` script to compile/build 4. Ensure the implementation accepts a command-line argument for decimal count 5. Test with `./test.sh` ## License This project is open source and available under the MIT License. ## Acknowledgments - Machin's formula for efficient π calculation - All language maintainers and contributors - Apple A18 Pro hardware for benchmarking