Scalene: Line-Level CPU and Memory Profiler
Scalene is a sampling profiler that reports CPU time and memory activity by source line and function. It runs inside the target process, combining timing estimates with allocation and copying measurements to distinguish expensive Python code from work performed by native libraries.
Unlike py-spy and Austin, Scalene requires starting the program under the profiler. It suits local diagnosis when a source-level report is more useful than attaching to an existing production process. Compare Python profilers for the alternatives.
Install alongside project dependencies
In an existing uv project:
uv add --dev scaleneThe --dev flag marks Scalene as a development dependencyA package needed during development (testing, linting, formatting) that is not shipped to users of your project. Installed with the --dev flag.
, keeping it out of production installs. uv adds Scalene to the development dependency group and installs it in the project’s virtual environment, alongside the application’s dependencies.
Scalene publishes packages for Linux, macOS, and Windows. Its package metadata requires Python 3.8 or newer, excluding Python 3.11.0; free-threaded builds have dedicated wheels for Python 3.13 and 3.14. Wheel availability and dependency compatibility vary by platform and interpreter. On Python 3.13t, the release notes require pydantic-core<=2.46.5; the PyPI file list lists available wheels.
Record and reopen a profile
For an existing program saved as script.py, run:
uv run scalene run script.pyuv run executes the command inside the project’s virtual environment, ensuring all dependencies are installed first. Scalene runs the program, then saves its measurements to scalene-profile.json in the current directory. Workloads must run long enough to collect samples; an empty profile does not establish that the program has no hotspots.
Display the saved report in the terminal:
uv run scalene view --cliThe output annotates source lines with timing and memory columns. To export a report with its viewer assets embedded:
uv run scalene view --standaloneThis writes scalene-profile.html, which opens locally without fetching the viewer’s assets from the internet.
| Option | Effect |
|---|---|
run --cpu-only |
Collect CPU measurements without memory or GPU profiling |
run -o results.json |
Save measurements under a specified filename |
run --profile-all |
Include source outside the entry-point directory, including libraries |
view results.json |
Open a specified saved profile |
Interpret the measurements
| Report | Meaning |
|---|---|
| Python, native, and system time | Estimated time attributed to Python execution, native code, and system activity |
| Memory | Sampled allocation activity, per-line memory growth, and memory trends |
| Copy volume | Bytes copied through observed copying operations, useful when data crosses Python/native boundaries |
| Await time | Wall-clock time attributed to the source line where a coroutine waits, with mean and peak concurrent waiters |
| Stack views | Combined Python/native call paths and a timeline for relating activity to the run |
Memory allocation measurements differ from resident memory, the physical pages currently held by the process. Austin’s memory mode measures changes in residency; the two reports answer different questions.
Async waits measure latency, not CPU consumption. A line with substantial waiting time may be waiting on a database or another task rather than performing expensive computation.
GPU measurements depend on the backend and accounting available on the machine. The project README documents NVIDIA GPU reporting; a utilization percentage is not a trace of individual GPU kernels. Per-process accounting and device-wide utilization must remain distinct when other processes share a GPU.
Account for attribution limits
The Python/native split is an estimate. Issue #428 reports misleading attribution in multithreaded code; the maintainer explains that worker threads use a different attribution method from the main thread. Validate a surprising split against the workload and available native-stack evidence before treating it as a reason to rewrite code.
Native stack capture is unavailable on Windows. On Apple M4 and newer chips with SME instructions, Scalene disables native stack capture to avoid a signal-handling problem that corrupts numerical state. CPU and line profiling use a separate sampling path on those machines, so the availability of line reports does not imply that native stack views are available.
Sampling does not count every call or allocation. Short operations may receive few samples, and profiling changes the workload’s execution costs. Compare repeated runs of the same workload when evaluating an optimization.
Request optional optimization suggestions
The local viewer works without a model provider. Its optional optimization controls send source text and measured line metrics to the selected provider, such as OpenAI, Amazon Bedrock, Azure, or a local model served through Ollama.
A local model keeps those requests on its configured endpoint; selecting a hosted provider sends them to that provider. Suggestions propose code changes, so correctness checks and another measured run are still needed to establish an improvement.