lazyline 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lazyline-0.1.0/PKG-INFO +461 -0
- lazyline-0.1.0/README.md +427 -0
- lazyline-0.1.0/pyproject.toml +103 -0
- lazyline-0.1.0/src/lazyline/__init__.py +5 -0
- lazyline-0.1.0/src/lazyline/__main__.py +567 -0
- lazyline-0.1.0/src/lazyline/discovery.py +371 -0
- lazyline-0.1.0/src/lazyline/export.py +178 -0
- lazyline-0.1.0/src/lazyline/memory.py +79 -0
- lazyline-0.1.0/src/lazyline/models.py +54 -0
- lazyline-0.1.0/src/lazyline/parallel.py +253 -0
- lazyline-0.1.0/src/lazyline/profiling.py +589 -0
- lazyline-0.1.0/src/lazyline/py.typed +0 -0
- lazyline-0.1.0/src/lazyline/reporting.py +657 -0
- lazyline-0.1.0/src/lazyline/subproc.py +302 -0
lazyline-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lazyline
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Zero-config line-level profiler for Python packages — no decorators, no code changes.
|
|
5
|
+
Keywords: profiler,profiling,line-profiler,performance,benchmark,optimization,bottleneck,zero-config
|
|
6
|
+
Author: Tomáš Venkrbec
|
|
7
|
+
Author-email: Tomáš Venkrbec <venkrbec.tomas@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Software Development :: Debuggers
|
|
19
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
20
|
+
Classifier: Topic :: Software Development :: Testing
|
|
21
|
+
Classifier: Topic :: System :: Benchmark
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Dist: typer>=0.15
|
|
24
|
+
Requires-Dist: line-profiler>=4.1.0
|
|
25
|
+
Requires-Dist: pygments>=2.14.0 ; extra == 'color'
|
|
26
|
+
Requires-Python: >=3.10
|
|
27
|
+
Project-URL: Homepage, https://github.com/TomasVenkrbec/lazyline
|
|
28
|
+
Project-URL: Repository, https://github.com/TomasVenkrbec/lazyline
|
|
29
|
+
Project-URL: Documentation, https://github.com/TomasVenkrbec/lazyline#readme
|
|
30
|
+
Project-URL: Changelog, https://github.com/TomasVenkrbec/lazyline/blob/main/CHANGELOG.md
|
|
31
|
+
Project-URL: Issues, https://github.com/TomasVenkrbec/lazyline/issues
|
|
32
|
+
Provides-Extra: color
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# Lazyline
|
|
36
|
+
|
|
37
|
+
[](https://pypi.org/project/lazyline/)
|
|
38
|
+
[](https://pypi.org/project/lazyline/)
|
|
39
|
+
[](https://github.com/TomasVenkrbec/lazyline/actions/workflows/ci.yml)
|
|
40
|
+
[](https://codecov.io/gh/TomasVenkrbec/lazyline)
|
|
41
|
+
[](LICENSE)
|
|
42
|
+
|
|
43
|
+
**Zero-config line-level profiler for Python packages.**
|
|
44
|
+
Point it at a package, give it a command, get a ranked line-by-line breakdown.
|
|
45
|
+
No `@profile` decorators. No code changes. No guessing.
|
|
46
|
+
|
|
47
|
+
## Why Lazyline?
|
|
48
|
+
|
|
49
|
+
### The problem
|
|
50
|
+
|
|
51
|
+
Finding line-level bottlenecks in a Python package typically means
|
|
52
|
+
decorating suspect functions with `@profile`, running `kernprof`,
|
|
53
|
+
reading the output, removing the decorators, and repeating until
|
|
54
|
+
you find the real culprit. If you guess wrong, you waste a cycle.
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Without lazyline — manual, iterative workflow:
|
|
58
|
+
# 1. Guess which functions might be slow
|
|
59
|
+
# 2. Add @profile decorators to each one
|
|
60
|
+
# 3. Run: LINE_PROFILE=1 python script.py or kernprof -lv script.py.
|
|
61
|
+
# 4. Read output, realize the bottleneck is elsewhere
|
|
62
|
+
# 5. Remove decorators, add new ones, go to step 3
|
|
63
|
+
# 6. Clean up all decorators when done
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Lazyline eliminates this loop. Point it at a package, give it a
|
|
67
|
+
command, and every function is profiled automatically:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# With lazyline — one command, done:
|
|
71
|
+
lazyline run my_package -- pytest tests/
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
No decorators. No code changes. No guessing.
|
|
75
|
+
|
|
76
|
+
### What lazyline adds over raw line_profiler
|
|
77
|
+
|
|
78
|
+
Lazyline wraps `line_profiler` and adds everything needed to go
|
|
79
|
+
from "I want to profile this package" to "here are the bottlenecks"
|
|
80
|
+
in a single command:
|
|
81
|
+
|
|
82
|
+
#### Zero-config profiling
|
|
83
|
+
|
|
84
|
+
- No `@profile` decorators — every function in the target scope
|
|
85
|
+
is discovered and instrumented automatically
|
|
86
|
+
- Automatic module and namespace package discovery (point at a
|
|
87
|
+
package name, directory, or `.py` file)
|
|
88
|
+
- `lru_cache` and other C-extension wrappers auto-unwrapped
|
|
89
|
+
|
|
90
|
+
#### Subprocess and worker profiling
|
|
91
|
+
|
|
92
|
+
- Child Python processes (e.g., Celery workers, Airflow tasks)
|
|
93
|
+
profiled via `sitecustomize.py` injection — no configuration needed
|
|
94
|
+
- `concurrent.futures.ProcessPoolExecutor` and `multiprocessing.Pool`
|
|
95
|
+
workers profiled with per-worker instances and merged results
|
|
96
|
+
|
|
97
|
+
#### Rich terminal output
|
|
98
|
+
|
|
99
|
+
- Syntax-highlighted source code (Pygments, monokai theme)
|
|
100
|
+
- Adaptive column widths that fit your terminal
|
|
101
|
+
- Compact mode (default) collapses un-hit lines
|
|
102
|
+
- Auto-scaling time units (s/ms/us/ns)
|
|
103
|
+
|
|
104
|
+
#### Analysis workflow
|
|
105
|
+
|
|
106
|
+
- `--top N`, `--filter`, `--summary` to focus on what matters
|
|
107
|
+
- JSON export/import for sharing and later analysis
|
|
108
|
+
- Optional `tracemalloc` memory tracking (`--memory`)
|
|
109
|
+
|
|
110
|
+
### When to use lazyline
|
|
111
|
+
|
|
112
|
+
Use lazyline when you need **exact, line-level timing** and want to
|
|
113
|
+
find bottlenecks without modifying code. It is especially useful
|
|
114
|
+
for profiling packages you don't own or can't easily change.
|
|
115
|
+
|
|
116
|
+
If you need **low-overhead production profiling**, a sampling
|
|
117
|
+
profiler like Scalene or py-spy is a better fit — they trade
|
|
118
|
+
line-level precision for significantly lower overhead.
|
|
119
|
+
|
|
120
|
+
## Quick Start
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
pip install lazyline
|
|
124
|
+
|
|
125
|
+
# Profile a package while running a script:
|
|
126
|
+
lazyline run my_package -- python evaluate.py --dataset "some_dataset"
|
|
127
|
+
|
|
128
|
+
# Profile a package while running its CLI tool:
|
|
129
|
+
lazyline run my_package -- my_package_cli run --verbose
|
|
130
|
+
|
|
131
|
+
# Profile a package while running its test suite:
|
|
132
|
+
lazyline run my_package -- pytest tests/
|
|
133
|
+
|
|
134
|
+
# Profile any importable package — no code changes needed:
|
|
135
|
+
lazyline run json -- python -c "import json; json.dumps([1, 2, 3])"
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Lazyline discovers all modules in the given scope, instruments
|
|
139
|
+
(attaches timing to) every Python function, runs the command,
|
|
140
|
+
and prints a ranked breakdown:
|
|
141
|
+
|
|
142
|
+
```text
|
|
143
|
+
Discovered 12 module(s) in scope 'my_package'.
|
|
144
|
+
Registered 89 function(s) for profiling.
|
|
145
|
+
|
|
146
|
+
=================================================
|
|
147
|
+
Lazyline results for my_package
|
|
148
|
+
3 of 89 functions called | Total: 12.4451s | Wall time: 10.2300s | Unit: s
|
|
149
|
+
|
|
150
|
+
Summary
|
|
151
|
+
|
|
152
|
+
Function Total (s) % Total Calls Time/Call (s)
|
|
153
|
+
-----------------------------------------------------------------------------------------------------------------
|
|
154
|
+
my_package.process.transform 8.3172 66.8% 500 0.016634
|
|
155
|
+
my_package.io.load_data 3.1245 25.1% 1 3.124500
|
|
156
|
+
my_package.utils.normalize 1.0034 8.1% 50000 0.000020
|
|
157
|
+
...
|
|
158
|
+
-----------------------------------------------------------------------------------------------------------------
|
|
159
|
+
Total 12.4451
|
|
160
|
+
|
|
161
|
+
Functions
|
|
162
|
+
|
|
163
|
+
my_package.process.transform (.../process.py:42)
|
|
164
|
+
8.3172s total | 500 calls | 0.016634s/call
|
|
165
|
+
|
|
166
|
+
Line Hits Time (s) Time/Hit (s) % Func Source
|
|
167
|
+
----------------------------------------------------------------------------------------
|
|
168
|
+
42 def transform(data):
|
|
169
|
+
43 500 0.031200 0.000062 0.4% result = []
|
|
170
|
+
44 500000 7.982100 0.000016 96.0% for row in data:
|
|
171
|
+
45 500000 0.301200 0.000001 3.6% result.append(row)
|
|
172
|
+
46 500 0.002700 0.000005 0.0% return result
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Installation
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
pip install lazyline
|
|
179
|
+
|
|
180
|
+
# With syntax highlighting (recommended):
|
|
181
|
+
pip install lazyline[color]
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Requires Python 3.10+. The target package must be importable
|
|
185
|
+
(installed or on `sys.path`) in the same environment. The `[color]`
|
|
186
|
+
extra installs Pygments for syntax-highlighted source in terminal
|
|
187
|
+
output (falls back to plain text if not installed).
|
|
188
|
+
|
|
189
|
+
## Comparison with Alternatives
|
|
190
|
+
|
|
191
|
+
| Tool | Granularity | Method | Code changes? | Subprocess profiling |
|
|
192
|
+
|------|-------------|--------|---------------|----------------------|
|
|
193
|
+
| **lazyline** | Per-line | Deterministic | None | Automatic |
|
|
194
|
+
| `kernprof` / `line_profiler` | Per-line | Deterministic | `@profile` decorators | No |
|
|
195
|
+
| `cProfile` | Per-function | Deterministic | None | No |
|
|
196
|
+
| `Scalene` | Per-line | Sampling | None | Yes |
|
|
197
|
+
| `py-spy` | Per-line (sampled) | Sampling | None (attach) | Yes (follow-children) |
|
|
198
|
+
|
|
199
|
+
**Unique:** Lazyline is the only line-level profiler that
|
|
200
|
+
automatically profiles `ProcessPoolExecutor`, `multiprocessing.Pool`,
|
|
201
|
+
and child Python processes (e.g., Celery workers, Airflow tasks)
|
|
202
|
+
without any configuration.
|
|
203
|
+
|
|
204
|
+
**Deterministic vs sampling:** Deterministic tracing (lazyline,
|
|
205
|
+
line_profiler, cProfile) fires a callback on every line or function
|
|
206
|
+
call, measuring exact execution counts and times. Sampling profilers
|
|
207
|
+
(Scalene, py-spy) periodically interrupt the program and record
|
|
208
|
+
where it is — much lower overhead, but statistical approximations
|
|
209
|
+
rather than exact counts.
|
|
210
|
+
|
|
211
|
+
Lazyline's deterministic approach means **relative rankings are
|
|
212
|
+
reliable** (if function A appears 10x slower than B, that ratio
|
|
213
|
+
holds), but **absolute times are inflated** by tracing overhead.
|
|
214
|
+
See [Overhead and Limitations](#overhead-and-limitations) for
|
|
215
|
+
details.
|
|
216
|
+
|
|
217
|
+
## Usage
|
|
218
|
+
|
|
219
|
+
### `lazyline run`
|
|
220
|
+
|
|
221
|
+
```text
|
|
222
|
+
lazyline run [OPTIONS] SCOPE [SCOPE...] [--] COMMAND [ARGS...]
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Profile a command, instrumenting all functions in the given scope(s).
|
|
226
|
+
|
|
227
|
+
| Option | Description |
|
|
228
|
+
|--------|-------------|
|
|
229
|
+
| `--top N` / `-n N` | Show only the N slowest functions |
|
|
230
|
+
| `--memory` | Enable tracemalloc memory tracking |
|
|
231
|
+
| `--output FILE` / `-o FILE` | Export results to JSON (`-` for stdout) |
|
|
232
|
+
| `--compact/--full` | Collapse un-hit lines (default) or show all |
|
|
233
|
+
| `--summary` | Print only the summary table, no per-line detail |
|
|
234
|
+
| `--filter PATTERN` / `-f PATTERN` | Only show functions matching fnmatch pattern(s) (comma-separated) |
|
|
235
|
+
| `--quiet` / `-q` | Suppress discovery/registration stderr messages |
|
|
236
|
+
| `--unit UNIT` | Time display unit: `auto` (default), `s`, `ms`, `us`, or `ns` |
|
|
237
|
+
|
|
238
|
+
Options can appear before or after SCOPE. When placed after SCOPE,
|
|
239
|
+
a `--` separator before COMMAND is required:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
lazyline run --top 5 my_package -- pytest tests/ # options before scope
|
|
243
|
+
lazyline run my_package --top 5 -- pytest tests/ # options after scope
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Multiple scopes can be profiled in a single run (requires `--`):
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
lazyline run utils.py my_package -- python script.py
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### `lazyline show`
|
|
253
|
+
|
|
254
|
+
```text
|
|
255
|
+
lazyline show FILE [--top N] [--compact/--full] [--summary] [--filter PATTERN] [--unit UNIT]
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Display profiling results from a previously saved JSON file.
|
|
259
|
+
|
|
260
|
+
### `lazyline --version`
|
|
261
|
+
|
|
262
|
+
Print the installed version and exit.
|
|
263
|
+
|
|
264
|
+
## Scope
|
|
265
|
+
|
|
266
|
+
Unlike tools like cProfile that profile everything, lazyline
|
|
267
|
+
focuses on specific code you choose — this keeps output clean
|
|
268
|
+
and overhead low.
|
|
269
|
+
|
|
270
|
+
SCOPE tells lazyline which package or module to profile. It
|
|
271
|
+
accepts three formats:
|
|
272
|
+
|
|
273
|
+
| Format | Example | What it does |
|
|
274
|
+
|--------|---------|--------------|
|
|
275
|
+
| Dotted module path | `my_package` | Imports and walks all submodules |
|
|
276
|
+
| Directory path | `my_package/utils` | Converted to dotted path, then walked |
|
|
277
|
+
| Single module | `json` | Imports that module only (+ submodules) |
|
|
278
|
+
| Single `.py` file | `utils.py` | Imports the file directly (no `sys.path` needed) |
|
|
279
|
+
|
|
280
|
+
Lazyline discovers modules via `pkgutil.walk_packages()` for
|
|
281
|
+
regular packages and filesystem scanning for implicit namespace
|
|
282
|
+
packages (directories without `__init__.py`). Every Python
|
|
283
|
+
function found is registered. C extension functions are silently
|
|
284
|
+
skipped.
|
|
285
|
+
|
|
286
|
+
## Commands
|
|
287
|
+
|
|
288
|
+
COMMAND is what lazyline executes under profiling. Supported forms:
|
|
289
|
+
|
|
290
|
+
| Form | Example |
|
|
291
|
+
|------|---------|
|
|
292
|
+
| Bare module name | `pytest tests/` |
|
|
293
|
+
| Console script (incl. hyphens) | `my-tool run-all` |
|
|
294
|
+
| `python -m module` | `python -m pytest -q` |
|
|
295
|
+
| Script file | `python script.py` |
|
|
296
|
+
| Inline code | `python -c "import json; json.dumps(1)"` |
|
|
297
|
+
|
|
298
|
+
Hyphenated console scripts (e.g., `my-tool`) are resolved
|
|
299
|
+
via `importlib.metadata` entry points automatically.
|
|
300
|
+
|
|
301
|
+
## Examples
|
|
302
|
+
|
|
303
|
+
Profile a package while running its test suite:
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
lazyline run --top 10 my_package -- pytest tests/
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Profile a CLI tool (console script with hyphens works too):
|
|
310
|
+
|
|
311
|
+
```bash
|
|
312
|
+
lazyline run my_package.cli -- my-tool run-all
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Profile with memory tracking:
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
lazyline run --memory my_package -- pytest tests/ -q
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Export results for later analysis:
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
lazyline run --output results.json my_package -- pytest -q
|
|
325
|
+
lazyline show results.json --top 10
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Filter to specific functions (supports comma-separated patterns):
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
lazyline run --filter "*extract*,*match*" my_package -- pytest tests/
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Profile a single `.py` file or multiple scopes in one run:
|
|
335
|
+
|
|
336
|
+
```bash
|
|
337
|
+
lazyline run utils.py -- python script.py
|
|
338
|
+
lazyline run utils.py my_package -- python evaluate.py
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## Output Format
|
|
342
|
+
|
|
343
|
+
Lazyline prints a results header, a summary table, and per-line detail:
|
|
344
|
+
|
|
345
|
+
**Results header** — scope, coverage, total time, wall-clock time, and display unit:
|
|
346
|
+
|
|
347
|
+
```text
|
|
348
|
+
=================================================
|
|
349
|
+
Lazyline results for pkg
|
|
350
|
+
2 of 15 functions called | Total: 1.4690s | Wall time: 1.2300s | Unit: s
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
**Summary table** — all profiled functions ranked by total time:
|
|
354
|
+
|
|
355
|
+
```text
|
|
356
|
+
Function Total (s) % Total Calls Time/Call (s)
|
|
357
|
+
-----------------------------------------------------------------------------------------------------------------
|
|
358
|
+
pkg.module.slow_func 1.2345 82.1% 100 0.012345
|
|
359
|
+
pkg.module.helper 0.2345 15.6% 1000 0.000235
|
|
360
|
+
...
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
**Per-line detail** — source code with timing for each function:
|
|
364
|
+
|
|
365
|
+
```text
|
|
366
|
+
pkg.module.slow_func (pkg/module.py:42)
|
|
367
|
+
1.2345s total | 100 calls | 0.012345s/call
|
|
368
|
+
|
|
369
|
+
Line Hits Time (s) Time/Hit (s) % Func Source
|
|
370
|
+
----------------------------------------------------------------------------------------
|
|
371
|
+
42 def slow_func(data):
|
|
372
|
+
43 100 0.000100 0.000001 0.0% result = []
|
|
373
|
+
44 100000 1.234000 0.000012 99.9% for item in data:
|
|
374
|
+
45 100000 0.000400 0.000000 0.0% result.append(item)
|
|
375
|
+
46 100 0.000000 0.000000 0.0% return result
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
On terminals, source code is syntax-highlighted (monokai theme) and
|
|
379
|
+
faint `│` column separators appear between numeric columns for easier
|
|
380
|
+
visual tracking across wide tables. Piped/file output uses plain text
|
|
381
|
+
with no ANSI formatting.
|
|
382
|
+
|
|
383
|
+
With `--memory`, an additional `Net Mem` column shows per-line
|
|
384
|
+
net memory allocation delta (bytes allocated minus freed).
|
|
385
|
+
|
|
386
|
+
## How It Works
|
|
387
|
+
|
|
388
|
+
1. **Discovery** — imports the target scope and walks all
|
|
389
|
+
submodules via `pkgutil.walk_packages()`, supplemented with
|
|
390
|
+
filesystem scanning for implicit namespace packages.
|
|
391
|
+
2. **Registration** — registers every Python function with
|
|
392
|
+
`line_profiler`'s `LineProfiler.add_module()`. C extensions
|
|
393
|
+
are skipped; `lru_cache` wrappers are auto-unwrapped.
|
|
394
|
+
3. **Execution** — runs the user's command with profiling
|
|
395
|
+
enabled. `line_profiler` uses `sys.monitoring` (Python 3.12+)
|
|
396
|
+
or `sys.settrace` for deterministic per-line tracing.
|
|
397
|
+
4. **Collection** — extracts per-line timing data, filters
|
|
398
|
+
out stdlib wrapper leaks via scope path matching.
|
|
399
|
+
5. **Memory** (optional) — `tracemalloc` takes before/after
|
|
400
|
+
snapshots and computes per-line net allocation deltas.
|
|
401
|
+
6. **Multiprocessing** — `concurrent.futures.ProcessPoolExecutor`
|
|
402
|
+
and `multiprocessing.Pool` worker processes are automatically
|
|
403
|
+
profiled with per-worker `LineProfiler` instances, stats
|
|
404
|
+
merged after execution.
|
|
405
|
+
7. **Subprocesses** — if the command spawns child Python
|
|
406
|
+
processes (e.g., Celery workers, Airflow tasks), lazyline injects
|
|
407
|
+
a `sitecustomize.py` bootstrap via `PYTHONPATH` so child
|
|
408
|
+
interpreters profile the same scope automatically.
|
|
409
|
+
|
|
410
|
+
## Overhead and Limitations
|
|
411
|
+
|
|
412
|
+
Lazyline uses **deterministic tracing** (exact measurement of every
|
|
413
|
+
line, as opposed to sampling which checks periodically). This fires
|
|
414
|
+
a callback on every line execution, which has important implications:
|
|
415
|
+
|
|
416
|
+
**Overhead inflates both wall-clock runtime and reported times.**
|
|
417
|
+
The callback cost is included in each line's measured time. For
|
|
418
|
+
functions with meaningful per-call work (>0.1ms), overhead is
|
|
419
|
+
negligible (~1.2x). For tight loops calling tiny functions millions
|
|
420
|
+
of times, overhead can be ~7x.
|
|
421
|
+
|
|
422
|
+
**Relative rankings are reliable.** If function A appears 10x
|
|
423
|
+
slower than function B, that ratio holds regardless of overhead.
|
|
424
|
+
Use lazyline to find *which* functions are slowest, not to measure
|
|
425
|
+
*absolute* execution time.
|
|
426
|
+
|
|
427
|
+
**Memory measurements are not inflated** by line-profiler tracing.
|
|
428
|
+
`tracemalloc` hooks the memory allocator separately. However,
|
|
429
|
+
allocation-heavy code (JSON serialization, string formatting) may
|
|
430
|
+
see significant wall-clock slowdown from tracemalloc's per-alloc
|
|
431
|
+
hooks. Use `--memory` only when you need allocation data.
|
|
432
|
+
|
|
433
|
+
Lazyline warns when a function exceeds 1M total line hits, as
|
|
434
|
+
reported times for such functions are unreliable.
|
|
435
|
+
|
|
436
|
+
Other limitations:
|
|
437
|
+
|
|
438
|
+
- `concurrent.futures.ProcessPoolExecutor` and `multiprocessing.Pool`
|
|
439
|
+
workers are profiled automatically (requires `fork` start method —
|
|
440
|
+
default on Linux; `spawn`/`forkserver` are not supported). Direct
|
|
441
|
+
`multiprocessing.Process` usage is not covered.
|
|
442
|
+
- Child processes started with `python -S` skip `sitecustomize.py`
|
|
443
|
+
loading, so subprocess profiling is silently disabled.
|
|
444
|
+
- C extension functions are skipped (no Python bytecode to trace).
|
|
445
|
+
- `tracemalloc` shows net allocation delta only — transient
|
|
446
|
+
allocations (alloc + free within the run) appear as ~0.
|
|
447
|
+
- `tracemalloc` adds ~30% memory overhead for its own bookkeeping.
|
|
448
|
+
|
|
449
|
+
See [`benchmarks/README.md`](benchmarks/README.md) for detailed
|
|
450
|
+
overhead measurements and methodology.
|
|
451
|
+
|
|
452
|
+
## Development
|
|
453
|
+
|
|
454
|
+
```bash
|
|
455
|
+
./docker/build.sh
|
|
456
|
+
./docker/run.sh
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
See [`CLAUDE.md`](CLAUDE.md) for project structure and
|
|
460
|
+
conventions, [`DESIGN.md`](DESIGN.md) for architecture and
|
|
461
|
+
plan.
|