Line-by-line profiling#
scope-profiler can integrate line_profiler to give you per-source-line timing breakdowns of decorated functions, alongside the usual region-level statistics.
Installation#
pip install "scope-profiler[line-profiler]"
Enabling line profiling#
Pass use_line_profiler=True to setup():
from scope_profiler import ProfileManager
ProfileManager.setup(use_line_profiler=True)
This selects LineProfilerRegion for all regions. Each region records
nanosecond timestamps and enables line_profiler tracing for every
function registered via the @ProfileManager.profile decorator.
Example#
import math
import random
from scope_profiler import ProfileManager
ProfileManager.setup(use_line_profiler=True)
@ProfileManager.profile("compute")
def compute(N=50_000):
s = 0.0
for _ in range(N):
x = random.random()
s += math.sin(x) * math.sqrt(x + 1.0)
return s
@ProfileManager.profile("allocate")
def allocate(N=100_000):
a = [i * i for i in range(N)]
b = []
for i in range(N):
b.append(i * i)
return a, b
compute()
allocate()
ProfileManager.finalize()
Output:
Region: compute
Total Calls : 1
Total Time : 0.033359 s
...
----------------------------------------
Region: allocate
Total Calls : 1
Total Time : 0.042457 s
...
----------------------------------------
Timer unit: 1e-09 s
Total time: 0.029169 s
File: examples/ex_line_profiling.py
Function: compute at line 10
Line # Hits Time Per Hit % Time Line Contents
==============================================================
10 @ProfileManager.profile("compute")
11 def compute(N=50_000):
12 1 1000.0 1000.0 0.0 s = 0.0
13 50001 8581000.0 171.6 29.4 for _ in range(N):
14 50000 9370000.0 187.4 32.1 x = random.random()
15 50000 11215000.0 224.3 38.4 s += math.sin(x) * math.sqrt(x + 1.0)
16 1 2000.0 2000.0 0.0 return s
The line-by-line table shows, for each source line:
Column |
Meaning |
|---|---|
Hits |
Number of times the line was executed |
Time |
Total time spent on that line (in timer units) |
Per Hit |
Average time per execution |
% Time |
Fraction of the function’s total time |
Decorator vs. context manager#
Decorator (
@ProfileManager.profile) — automatically registers the function withline_profiler. This is the primary use case.Context manager (
with ProfileManager.profile_region()) — enables/disables the profiler around the block. Any functions previously registered via the decorator path will be profiled while the context is active. The code inside thewithblock itself is not line-profiled (line_profiler needs a function reference).
Accessing stats programmatically#
region = ProfileManager.get_region("compute")
# Get the line_profiler stats object
stats = region.get_stats()
# Print formatted output
region.print_stats()
Overhead considerations#
Line profiling adds ~40 µs per call because line_profiler instruments
every source line in the function. It is designed for targeted
debugging, not for always-on use in hot loops. See Profiling overhead
for benchmark data.