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