Speeding up python through profiling

Python is a shockingly slow language. A test on a raspberry pi of simply “turn this pin on and off as fast as you can” gave the results below.

SystemLibrarySpeed
Shell/proc/mem access2.8 kHz
Shell / gpio utilityWiringPi gpio utility40 Hz
PythonRPI.GPIO70 kHz
PythonwiringPi2 bindings28 kHz
RubywiringPi bindings21 kHz
CNative library22 MHz
CBCM 28355.4 MHz
CwiringPi4.1 – 4.6 MHz
PerlBCM 283548 kHz

So, how do we speed up python? The simple answer is not to use python, and use a faster language. Whilst that may sound facetious, some of the most commonly used python libraries are actually written in, or have cores in much faster languages.

Library~ % PythonNotes
PyTorch62Core in C++
Numpy60Core in C
TensorFlow20Core in C++
OpenCV3Core in C++

If using pre-made libraries with fast cores still doesn’t provide the speed-up you need, then what? Find the slow bit – Test it,  Tidy it,  Time it.
Test it – Quite often, things are slow because they’re a bit buggy. So a bit more of a concerted effort testing the code wouldn’t go amiss.
Tidy it – If the code’s spaghetti-like and has lost its elegance as you’ve had to put in workarounds for workarounds, perhaps it’s going through seldom used pathways and incurring an efficiency hit every time. Refactoring your code can help its performance.
Time it – If the above are no help, time to reach for the big guns and time each section of code and see where the slow-down is happening. This can be done with a profiler and/or a visualiser, a selection of which are mentioned below.

  • Timeit – Measures the execution time of small code snippets
  • Profile – Collect detailed runtime statistics (consider using cProfile or PyInstrument)
  • cProfile
    • Similar to Profile, but better.
    • Deterministic profiler. 
    • Monitors every function call, return and exception event, records timing for each.
  • PyInstrument
    • Low overhead profiler and visualiser
    • Statistical profiler. 
    • Will miss things, but mainly the things which are inherently quick anyway.
    • Measures at repeating intervals which function is being called.

I would recommend a look at PyInstrument

PyInstrument can be called from the command line as a simple module, where you also specify an HTML file as interactive output on how your code is running.

python3 -m pyinstrument  -o count-char.html count-char.py

PyInstrument also provides some fairly easy to read visualisations of your code.

Or even more complex code such as:

Python visualisers

There are some other options which involve very little code change.

PyPy
Alternative interpreter and JIT compiler
Best for long-running code

Enable JIT
Available in v3.13+
activate using –enable-experimental-jit 

Numba
Works best on code that uses NumPy and code can be dramatically sped up just by adding decorators such as
@jit decorator
@njit(parallel=True)

Author