Ein Anderssono c989bb8cb4 Convert README to English with natural flow
- Recreated entire README in English
- Preserved all technical details and data
- Improved flow and readability
- Kept Swedish version as README_SV.md for reference
- All charts and analysis now in English
2026-04-23 01:15:07 +02:00

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

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]
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

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]
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

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]
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

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)
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)
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)
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)
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)
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)
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)
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)
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)
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
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
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

./build.sh

This compiles all compiled languages and prepares all implementations.

Run Benchmarks

./run_all.sh <decimals>

Example:

./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

./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
S
Description
No description provided
Readme
2.3 MiB
Languages
Python 32.3%
Shell 26.3%
C 5.5%
Rust 2.7%
C# 2.7%
Other 30.5%