Performance Comparison: Python vs Rust for Fibonacci Calculation
When it comes to computational performance, choosing the right programming language can make a significant difference. This post explores a practical comparison between Python and Rust implementations of the Fibonacci sequence calculator, complete with instrumentation for precise performance measurements.
Project Structure
graph TD; A[Project Root] --> B[Python Implementation]; A --> C[Rust Implementation]; A --> D[Profiling Script]; B --> E[fib2.py]; C --> F[fib2.rs]; D --> G[profiler.sh]; G --> H[performance_metrics.csv];
Performance Overview
gantt title Performance Comparison: Python vs Rust (Fibonacci 40) dateFormat X axisFormat %ssection Python 7.47 seconds: 0, 7.47 section Rust 0.174 seconds: 0, 0.174
Code Implementation
Let's look at our baseline implementations. All code is available on Codeberg.
Python Implementation (fib2.py
)
import time
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
start_time = time.time()
fibonacci(40)
end_time = time.time()
execution_time = end_time - start_time
print(execution_time)
Rust Implementation (fib2.rs
)
use std::time::Instant;
fn fibonacci(n: u32) -> u32 {
match n {
0 => 0,
1 => 1,
_ => fibonacci(n - 1) + fibonacci(n - 2),
}
}
fn main() {
let start = Instant::now();
fibonacci(40);
let duration = start.elapsed();
println!("{:.4}", duration.as_secs_f64());
}
Profiling Script (profiler.sh
)
#!/bin/bash
# File to store performance metrics
output_file="performance_metrics.csv"
# Create the CSV file with headers if it doesn't exist
if [ ! -f "$output_file" ]; then
echo "Timestamp,Language,ExecutionTime" > "$output_file"
fi
# Number of iterations to run
iterations=10
# Function to run Python script and log execution time
run_python() {
start_time=$(date +%Y-%m-%dT%H:%M:%S.%3N)
execution_time=$(python3 fib2.py)
echo "$start_time,Python,$execution_time" >> "$output_file"
}
# Function to run Rust script and log execution time
run_rust() {
start_time=$(date +%Y-%m-%dT%H:%M:%S.%3N)
execution_time=$(./fib2)
echo "$start_time,Rust,$execution_time" >> "$output_file"
}
# Run Python and Rust scripts for specified iterations
for ((i=1; i<=iterations; i++))
do
echo "Running iteration $i..."
run_python
run_rust
done
echo "Performance metrics saved to $output_file"
Results Analysis
graph TD; subgraph Python[Python Execution Times] A[Run 1: 7.29s] --> B[Run 2: 7.38s]; B --> C[Run 3: 7.34s]; C --> D[Run 4: 7.41s]; D --> E[Run 5: 7.33s]; endsubgraph Rust[Rust Execution Times] F[Run 1: 0.174s] --> G[Run 2: 0.173s]; G --> H[Run 3: 0.174s]; H --> I[Run 4: 0.173s]; I --> J[Run 5: 0.175s]; end
Key Findings
graph LR; A[Performance Results] --> B[Python ~7.47s]; A --> C[Rust ~0.174s]; B --> D[Standard Library]; C --> E[Optimized Binary]; D --> F[CPU Bound]; E --> G[43x Faster];
Performance Analysis Setup
The experiment uses a bash-based profiler to ensure consistent timing measurements across both languages. This centralized approach provides several benefits:
- Unified timestamp generation
- Consistent metrics collection
- Language-agnostic measurement system
- Data format suitable for further analysis
Implementation Details
Both implementations calculate the 40th Fibonacci number using recursive approaches. The profiler executes each implementation 10 times and records:
- Timestamp of execution
- Programming language used
- Execution duration in seconds
Raw performance data sample:
2024-12-30T06:11:39.250,Python,7.291918516159058
2024-12-30T06:11:46.559,Rust,0.1744
2024-12-30T06:11:46.736,Python,7.383427619934082
2024-12-30T06:11:54.137,Rust,0.1739
Optimization Opportunities
For Python:
- Replace recursion with dynamic programming
- Consider using PyPy for improved performance
- Implement memoization
- Explore Numba or Cython for critical sections
For Rust:
- Use release mode compilation (
cargo build --release
) - Consider using u64 for larger numbers
- Explore const generics for compile-time evaluation
- Implement iterative solution for better memory usage
Conclusion
This performance comparison demonstrates the significant advantages of Rust for computationally intensive tasks, while also highlighting the importance of proper instrumentation and measurement in performance analysis. Even with unoptimized recursive implementations, the performance difference is substantial.