Profiling Your Decorators: Tools and Techniques
By khoanc, at: April 19, 2025, 7:48 p.m.
Estimated Reading Time: __READING_TIME__ minutes


Python decorators can be both a blessing and a curse. While they help you write DRY and modular code, they can also hide performance problems, especially when chained or applied blindly in production systems.
In this post, we’ll walk through how to profile decorators, which tools to use, and what signals to look for so you can avoid decorator-induced slowdowns in your Python application.
Why Profiling Decorators Matters
A single decorator might add only a few microseconds of overhead. But when:
-
You chain multiple decorators
-
They wrap thousands of function calls per second
-
They add logging, tracing, retries, metrics…
…the impact compounds fast.
Before blaming your database or CPU, check your decorators. They might be the silent bottleneck. Everything is described deeply in this post.
Tools You Can Use
1. timeit: Quick and Dirty Benchmarks
Perfect for microbenchmarks.
import timeit
def raw():
return 42
@my_custom_decorator
def decorated():
return 42
print("Raw:", timeit.timeit(raw, number=100000))
print("Decorated:",
timeit.timeit(decorated, number=100000))
2. cProfile + Snakeviz: See Where Time is Spent
python -m cProfile -o output.prof myscript.py
snakeviz output.prof
You’ll get a visual breakdown of function call timings. If your decorators are expensive, they’ll pop up in the call tree.
3. line_profiler: Zoom in on Specific Lines
Install via pip:
pip install line_profiler
Use @profile
to mark functions:
@profile
@log_execution
def process_data(): ...
Then run:
kernprof -l -v myscript.py
Great for pinpointing slow lines inside decorated functions.
4. Py-Spy: Non-Intrusive, Real-Time Profiling
Run your app and use:
py-spy top --pid <pid></pid>
Or generate flame graphs:
py-spy record -o profile.svg --pid <pid></pid>
Helpful when decorators add overhead but don’t show up in your codebase directly (e.g., via metaprogramming).
Red Flags to Watch For
-
Decorators that perform I/O, logging, or access databases.
-
Excessive use of
functools.lru_cache
with large cache sizes.
-
Decorators with try/except that swallow exceptions silently.
-
@retry
or@timeout
decorators that block execution waiting for timeout to expire.
-
Wrappers that don’t pass
*args, **kwargs
efficiently.
Pro Tips
-
Minimize decorators in hot paths (e.g., HTTP handlers or tight loops).
-
Test with and without decorators to see actual overhead.
-
Profile locally and in production with real traffic patterns.