Files
print_hej/README.md
T
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

16 KiB

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