Zig vs Python on Fibonacci

August 26, 2025

Today I was having some free time and I thought why not benchmark some basic code in Zig and Python.

After thinking a bit, Fibonacci it was and wrote code for both:

Fibonacci code in Zig:

const std = @import("std");
const print = std.debug.print;

// Simple iterative Fibonacci implementation
fn fibonacci(n: u64) u64 {
    if (n <= 1) return n;

    var a: u64 = 0;
    var b: u64 = 1;
    var i: u64 = 2;

    while (i <= n) : (i += 1) {
        const temp = a + b;
        a = b;
        b = temp;
    }

    return b;
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const args = try std.process.argsAlloc(allocator);
    defer std.process.argsFree(allocator, args);

    if (args.len != 2) {
        print("Usage: ./fibonacci <n>\n", .{});
        return;
    }

    const n = try std.fmt.parseInt(u64, args[1], 10);
    const result = fibonacci(n);
    print("{d}\n", .{result});
}

I don’t think there is zig formatter in notion which is sad.

Here is build.zig code

const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const root_module = b.createModule(.{
        .root_source_file = b.path("fibonacci.zig"),
        .target = target,
        .optimize = optimize,
    });

    const exe = b.addExecutable(.{
        .name = "fibonacci",
        .root_module = root_module,
    });

    b.installArtifact(exe);
}

Now here we have python code:

#!/usr/bin/env python3
"""
Simple Fibonacci implementation in Python for benchmarking
"""
import sys

def fibonacci(n):
    """Simple iterative Fibonacci implementation"""
    if n <= 1:
        return n

    a, b = 0, 1
    for _ in range(2, n + 1):
        a, b = b, a + b
    return b

def main():
    if len(sys.argv) != 2:
        print("Usage: python3 fibonacci.py <n>")
        sys.exit(1)

    n = int(sys.argv[1])
    result = fibonacci(n)
    print(result)

if __name__ == "__main__":
    main()

For benchmarking, I created a simple shell script which uses time command.

benchmark.sh

#!/bin/bash

# Simple Fibonacci Benchmark Script
# Compares Python vs Zig implementations

set -e

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

echo -e "${BLUE}=== Simple Fibonacci Benchmark ===${NC}"

# Build Zig executable
echo -e "\n${YELLOW}Building Zig executable...${NC}"
cd zig
if ! zig build -Doptimize=ReleaseFast; then
    echo -e "${RED}Failed to build Zig executable${NC}"
    exit 1
fi
cd ..

# Test different values
TEST_CASES=(30 35 40 45)

echo -e "\n${BLUE}=== Basic Time Comparison ===${NC}"

for n in "${TEST_CASES[@]}"; do
    echo -e "\n${GREEN}Testing Fibonacci($n):${NC}"

    echo "  Python:"
    time python3 python/fibonacci.py $n

    echo "  Zig:"
    time ./zig/zig-out/bin/fibonacci $n

    echo "  ---"
done

# Detailed comparison with hyperfine (if available)
if command -v hyperfine &> /dev/null; then
    echo -e "\n${BLUE}=== Detailed Performance Analysis ===${NC}"

    hyperfine \
        --warmup 3 \
        --min-runs 10 \
        --max-runs 50 \
        --export-json "benchmarks/results.json" \
        "python3 python/fibonacci.py 40" \
        "./zig/zig-out/bin/fibonacci 40"

    echo -e "\n${YELLOW}Results exported to benchmarks/results.json${NC}"
else
    echo -e "\n${YELLOW}Install hyperfine for detailed benchmarking:${NC}"
    echo "  macOS: brew install hyperfine"
    echo "  Linux: apt install hyperfine"
fi

# Performance scaling test
echo -e "\n${BLUE}=== Performance Scaling Test ===${NC}"
SCALING_TESTS=(100 1000 10000)

echo -e "${GREEN}Testing large numbers:${NC}"
for n in "${SCALING_TESTS[@]}"; do
    echo -e "\n  ${YELLOW}n = $n${NC}"

    echo "    Python:"
    time python3 python/fibonacci.py $n > /dev/null

    echo "    Zig:"
    time ./zig/zig-out/bin/fibonacci $n > /dev/null
done

echo -e "\n${BLUE}=== Benchmark Complete ===${NC}"

Here are the results:

=== Simple Fibonacci Benchmark ===

Building Zig executable...

=== Basic Time Comparison ===

Testing Fibonacci(30):
  Python:
832040

real	0m0.040s
user	0m0.016s
sys	0m0.009s
  Zig:
832040

real	0m0.004s
user	0m0.001s
sys	0m0.001s
  ---

Testing Fibonacci(35):
  Python:
9227465

real	0m0.022s
user	0m0.015s
sys	0m0.005s
  Zig:
9227465

real	0m0.003s
user	0m0.001s
sys	0m0.001s
  ---

Testing Fibonacci(40):
  Python:
102334155

real	0m0.023s
user	0m0.015s
sys	0m0.005s
  Zig:
102334155

real	0m0.003s
user	0m0.001s
sys	0m0.001s
  ---

Testing Fibonacci(45):
  Python:
1134903170

real	0m0.022s
user	0m0.014s
sys	0m0.005s
  Zig:
1134903170

real	0m0.003s
user	0m0.001s
sys	0m0.001s
  ---

Install hyperfine for detailed benchmarking:
  macOS: brew install hyperfine
  Linux: apt install hyperfine

=== Performance Scaling Test ===
Testing large numbers:

  n = 100
    Python:

real	0m0.024s
user	0m0.015s
sys	0m0.005s
    Zig:
3736710778780434371

real	0m0.003s
user	0m0.001s
sys	0m0.001s

  n = 1000
    Python:

real	0m0.021s
user	0m0.014s
sys	0m0.005s
    Zig:
817770325994397771

real	0m0.003s
user	0m0.001s
sys	0m0.001s

  n = 10000
    Python:

real	0m0.024s
user	0m0.016s
sys	0m0.005s
    Zig:
15574651946073070043

real	0m0.003s
user	0m0.001s
sys	0m0.001s

=== Benchmark Complete ===

For large numbers, python didn’t give any output!!!

Although Zig is fast but why didn’t python give any output 🤔

We will debug this later…

till then, thanks for reading