Jump to content

Python Was Too Slow — Rust Fixed My Hot Code Paths

From JOHNWICK

Python was slowing me down. Critical loops, hot functions, heavy computations — I could feel the lag every time I ran my code. Seconds that should not have existed kept stacking up. I had two choices: tolerate the delay or rewrite the bottlenecks. I chose Rust.

By the time I finished, my Python scripts that once took 3.5 seconds now completed in 0.45 seconds. Yes, a seven-fold speedup.

Why Python Struggles in Hot Paths

Python excels in flexibility, readability, and rapid prototyping. However, when it comes to CPU-heavy loops or data transformations, it suffers because of:

  • Dynamic typing overhead
  • Global Interpreter Lock (GIL) limiting multi-threaded CPU usage
  • Inefficient memory handling in tight loops

For example, consider this Python snippet for computing large Fibonacci numbers:

def fib(n):

   if n <= 1:
       return n
   return fib(n-1) + fib(n-2)

import time start = time.time() fib(35) print("Time:", time.time() - start)

Result: 2.8 seconds for fib(35) on my laptop. Notice: every recursion multiplies work exponentially. Python cannot escape this without external libraries or rewriting.

Enter Rust: Zero-Cost Abstractions for Real Speed Rust gives you memory safety without garbage collection and performance comparable to C/C++.

The same Fibonacci function in Rust:

fn fib(n: u32) -> u64 {

   match n {
       0 => 0,
       1 => 1,
       _ => fib(n-1) + fib(n-2),
   }

}

fn main() {

   let start = std::time::Instant::now();
   fib(35);
   println!("Time: {:?}", start.elapsed());

}

Result: 0.44 seconds. That is a 7x speed improvement on pure CPU-bound work.

Step-by-Step Migration Strategy

When moving hot paths to Rust from Python, follow this simple framework:

  • Identify Hot Paths
  • Use profiling: cProfile, line_profiler, or PyInstrument
  • Focus on loops or functions that dominate CPU time
  • Write Minimal Rust Functions
  • Keep signatures simple
  • Avoid unnecessary generics for small hot functions
  • Bridge Python and Rust
  • Use PyO3 or Rust FFI
  • Replace only hot paths; leave Python for orchestration

Architecture Diagram

Python Application
+----------------+
| Main Program   |
| Orchestration  |
+--------+-------+
         |
         v
+--------+-------+
| Hot Path Module|
|   (Rust)       |
+----------------+
         |
         v
     Result Output

Explanation: 
The Python program orchestrates everything but delegates CPU-heavy operations to Rust, reducing execution time drastically.

Example

I migrated a data transformation pipeline processing 1 million records: Approach Time (s) Memory (MB) Python 38.2 512 Rust 4.9 312 Insight: Rust not only sped up the pipeline but also lowered memory usage. Minimal Rust Integration Example Suppose we want to compute square roots of large datasets:

pub fn compute_sqrt(data: Vec<f64>) -> Vec<f64> {

   data.iter().map(|x| x.sqrt()).collect()

}

And in Python using PyO3:

import rust_module

data = [i*0.5 for i in range(1_000_000)] result = rust_module.compute_sqrt(data)

Notice: No complicated setup, just a direct, high-performance replacement.

Key Takeaways

  • Python is excellent for high-level logic; Rust is perfect for hot paths.
  • Migrating only the bottlenecks preserves development speed while unlocking performance.
  • Minimal Rust integration yields dramatic speedups without rewriting your entire application.

Advice to Fellow Developers Stop tolerating unnecessary slowness. Your users notice, your pipelines lag, your iteration cycle suffers.

Start with one function, measure, and migrate. You will see instant, measurable gains.

Read the full article here: https://medium.com/@vishwajitpatil1224/python-was-too-slow-rust-fixed-my-hot-code-paths-26f493c5c08c