scalene

GitHub

Scalene: a high-performance, high-precision CPU, GPU, and memory profiler for Python with AI-powered optimization proposals

AI Prompts & Endpoints
Agent Skills View CodeWiki Knowledge Base

Repository: plasma-umass/scalene


Stars: 13367

CLAUDE.md

Scalene Development Guide

Project Overview

Scalene is a high-performance CPU, GPU, and memory profiler for Python with AI-powered optimization proposals. It runs significantly faster than other Python profilers while providing detailed performance information. See the paper docs/osdi23-berger.pdf for technical details on Scalene's design.

Key features:
- CPU, GPU (NVIDIA/Apple), and memory profiling
- AI-powered optimization suggestions (OpenAI, Anthropic, Azure, Amazon Bedrock, Gemini, Ollama)
- Web-based GUI and CLI interfaces
- Jupyter notebook support via magic commands (%scrun, %%scalene)
- Line-by-line profiling with low overhead
- Separates Python time from native/C time

Platform support: Linux, macOS, WSL 2 (full support); Windows (partial support)

Build & Test Commands

bash

Install in development mode


pip install -e .

Run all tests


python3 -m pytest tests/

Run tests for a specific Python version


python3.X -m pytest tests/

Run linters


mypy scalene
ruff check scalene

Run a single test file


python3 -m pytest tests/test_coverup_83.py -v

Project Structure

Core Profiler Components (scalene/)

- scalene_profiler.py - Main profiler class (Scalene). Entry point for profiling. Uses signal-based sampling for CPU profiling. Coordinates all profiling subsystems.
- scalene_statistics.py - ScaleneStatistics class. Collects and aggregates profiling data. Key types: ProfilingSample, MemcpyProfilingSample. Uses RunningStats for statistical aggregation.
- scalene_output.py - Profile output formatting for CLI/HTML
- scalene_json.py - ScaleneJSON class for JSON output format
- scalene_analysis.py - Profile analysis logic

Entry Points

- __main__.py - Entry point for python -m scalene
- profile.py - Entry point for --on/--off control of background profiling

Configuration & Arguments

- scalene_config.py - Version info (scalene_version, scalene_date) and constants:
- SCALENE_PORT = 11235 - Default port for web UI
- NEWLINE_TRIGGER_LENGTH - Must match src/include/sampleheap.hpp
- scalene_arguments.py - ScaleneArguments class (extends argparse.Namespace) with all profiler options and their defaults defined in ScaleneArgumentsDict
- scalene_parseargs.py - ScaleneParseArgs.parse_args() builds the argument parser. RichArgParser provides colored help output (uses Rich on Python < 3.14, native argparse colors on 3.14+)

Signal Handling

- scalene_signals.py - Signal definitions for CPU sampling
- scalene_signal_manager.py - Manages signal handlers
- scalene_sigqueue.py - Signal queue management
- scalene_client_timer.py - Timer for periodic profiling

GPU Support

- scalene_nvidia_gpu.py - NVIDIA GPU profiling via pynvml
- scalene_apple_gpu.py - Apple GPU profiling (Metal)
- scalene_accelerator.py - Generic accelerator interface
- scalene_neuron.py - AWS Neuron support

Memory Profiling

- scalene_memory_profiler.py - Memory profiling logic
- scalene_leak_analysis.py - Memory leak detection (experimental, --memory-leak-detector)
- scalene_mapfile.py - ScaleneMapFile for memory-mapped communication with native extension
- scalene_preload.py - Sets up LD_PRELOAD/DYLD_INSERT_LIBRARIES for native memory tracking

Jupyter Integration

- scalene_magics.py - Jupyter magic commands (%scrun for line mode, %%scalene for cell mode)
- scalene_jupyter.py - Jupyter notebook support utilities

Replacement Modules (replacement_.py)

These modules monkey-patch standard library functions to capture profiling data during blocking operations:
- replacement_fork.py - Tracks os.fork()
- replacement_exit.py - Tracks sys.exit()
- replacement_lock.py, replacement_mp_lock.py, replacement_sem_lock.py - Lock acquisition timing
- replacement_thread_join.py, replacement_pjoin.py - Thread/process join timing
- replacement_signal_fns.py - Signal function replacements
- replacement_poll_selector.py - I/O polling timing
- replacement_get_context.py - Multiprocessing context

Utilities

- runningstats.py - RunningStats class for online statistical calculations (mean, variance)
- scalene_funcutils.py - Function utilities
- scalene_utility.py - General utilities
- sparkline.py - Sparkline generation for memory visualization
- syntaxline.py - Syntax-highlighted source code lines
- adaptive.py - Adaptive sampling logic
- time_info.py - Time measurement utilities
- sorted_reservoir.py - Reservoir sampling for bounded-size sample collection

GUI (scalene/scalene-gui/)

Web-based GUI built with TypeScript, bundled with esbuild.

Core Files:
- index.html.template - Jinja2 template for main GUI page (rendered by scalene_utility.py)
- scalene-gui.ts - Main TypeScript entry point, UI event handlers, initialization
- scalene-gui-bundle.js - Bundled JavaScript output (generated, do not edit directly)

AI Provider Modules:
- openai.ts - OpenAI API integration (sendPromptToOpenAI, fetchOpenAIModels)
- anthropic.ts - Anthropic Claude API integration
- gemini.ts - Google Gemini API integration (sendPromptToGemini, fetchGeminiModels)
- optimizations.ts - Provider dispatch logic, prompt generation
- persistence.ts - localStorage persistence with environment variable fallbacks

Support Files:
- launchbrowser.py - Opens browser to GUI (default port 11235)
- find_browser.py - Cross-platform browser detection

Vendored Assets (for offline support):
- jquery-3.6.0.slim.min.js - jQuery (vendored locally, not loaded from CDN)
- bootstrap.min.css - Bootstrap 5.1.3 CSS
- bootstrap.bundle.min.js - Bootstrap 5.1.3 JS with Popper
- prism.css - Syntax highlighting styles
- favicon.ico - Scalene favicon
- scalene-image.png - Scalene logo

These assets are copied to a temp directory when serving via HTTP, enabling the GUI to work in air-gapped/offline environments.

Building the GUI:

bash
cd scalene/scalene-gui
npx esbuild scalene-gui.ts --bundle --outfile=scalene-gui-bundle.js --format=iife --global-name=ScaleneGUI

Native Extensions (src/)

C++ code for low-overhead memory allocation tracking:

Headers (src/include/):
- sampleheap.hpp - Sampling heap allocator. Key constant NEWLINE must match Python config.
- memcpysampler.hpp - Intercepts memcpy to track copy volume
- pywhere.hpp - Tracks Python file/line info for allocations
- samplefile.hpp - File-based communication with Python
- sampler.hpp, poissonsampler.hpp, thresholdsampler.hpp - Sampling strategies
- scaleneheader.hpp - Common header definitions

Sources (src/source/):
- libscalene.cpp - Main native library (loaded via LD_PRELOAD)
- pywhere.cpp - Python location tracking implementation
- get_line_atomic.cpp - Atomic line number access
- traceconfig.cpp - Trace configuration

Vendor Libraries (vendor/)

- Heap-Layers/ - Memory allocator infrastructure (by Emery Berger)
- printf/ - Async-signal-safe printf implementation

Key Patterns

Python Version Compatibility

The codebase supports Python 3.8-3.14. Version-specific code uses:

python
if sys.version_info >= (3, 14):
# Python 3.14+ specific code
else:
# Older Python versions

Type Annotation Compatibility (Python 3.8/3.9):
- Do NOT use X | Y union syntax in runtime-evaluated annotations (PEP 604 requires Python 3.10+). Use Optional[X] or Union[X, Y] from typing instead.
- Do NOT use list[X], dict[K, V], tuple[X, ...] in runtime-evaluated annotations (PEP 585 lowercase generics require Python 3.9+). Use List, Dict, Tuple from typing for 3.8 support.
- Adding from __future__ import annotations makes all annotations strings (not evaluated at runtime), which allows modern syntax on older Python. However, this can break code that inspects annotations at runtime (e.g., dataclasses, pydantic).
- The safest approach for this codebase: use typing.Optional, typing.Union, typing.List, typing.Tuple, typing.Dict in all annotation positions that are evaluated at runtime (function signatures, variable annotations outside if TYPE_CHECKING blocks).

Python 3.13 Changes (dis module):
- dis.Instruction.starts_line changed from int | None (line number) to bool
- New dis.Instruction.line_number attribute (int | None) added for the actual line number
- On Python < 3.13, starts_line is only set on the first instruction of each source line; use a line-tracking loop to propagate line numbers to subsequent instructions

Bytecode/Opcode Compatibility (dis module):
- Never match specific opcode names (e.g., JUMP_BACKWARD, JUMP_ABSOLUTE, POP_JUMP_IF_TRUE). Opcode names change across Python versions — for example, Python 3.10 while loops use POP_JUMP_IF_TRUE for backward jumps, Python 3.11+ uses JUMP_BACKWARD, and JUMP_ABSOLUTE was removed in 3.12.
- Always use abstract dis module categories when possible: dis.hasjabs (absolute jump opcodes), dis.hasjrel (relative jump opcodes), dis.hasconst, dis.hasname, etc. These are maintained by CPython and work across all versions.
- For call detection, matching opname.startswith("CALL") is acceptable since that prefix has been stable, but prefer opcode integer sets over name strings for hot paths.
- When checking jump direction (forward vs backward), use instr.argval (which dis resolves to an absolute offset) and compare against instr.offset, rather than relying on opcode names to imply direction.

Python 3.14 Changes:
- argparse now has built-in colored help output (color=True parameter)
- RichArgParser uses Rich for colors on Python < 3.14, native argparse colors on 3.14+

Argument Parsing (scalene_parseargs.py)

python
class RichArgParser(argparse.ArgumentParser):
"""ArgumentParser that uses Rich for colored output on Python < 3.14."""

def __init__(self, args, *kwargs):
if sys.version_info < (3, 14):
from rich.console import Console
self._console = Console()
else:
self._console = None
super().__init__(args, *kwargs)

The _colorize_help_for_rich() function applies Python 3.14-style colors using Rich markup:
- usage: and options: → bold blue
- Program name → bold magenta
- Long options (--foo) → bold cyan
- Short options (-h) → bold green
- Metavars (FOO) → bold yellow

GUI Patterns

Preventing Browser Password Prompts:
Use autocomplete="one-time-code" on password/API key inputs to prevent browsers from offering to save them:

html
<input type="password" id="api-key" autocomplete="one-time-code">

Show/Hide Password Toggle:

typescript
function togglePassword(inputId: string, button: HTMLButtonElement): void {
const input = document.getElementById(inputId) as HTMLInputElement;
if (input.type === "password") {
input.type = "text";
button.textContent = "Hide";
} else {
input.type = "password";
button.textContent = "Show";
}
}

Provider Field Visibility:
Use CSS classes to show/hide provider-specific fields:

typescript
function toggleServiceFields(): void {
const service = (document.getElementById("service") as HTMLSelectElement).value;
// Hide all provider sections
document.querySelectorAll(".provider-section").forEach((el) => {
(el as HTMLElement).style.display = "none";
});
// Show selected provider section
const section = document.querySelector(.${service}-fields);
if (section) (section as HTMLElement).style.display = "block";
}

Persistent Form Elements:
Add class persistent to inputs that should be saved/restored from localStorage:

html
<input type="text" id="api-key" class="persistent">

The persistence.ts module handles save/restore automatically.

Standalone HTML Generation:
The generate_html() function in scalene_utility.py supports a standalone parameter:
- When standalone=False (default): Assets are referenced as local files (e.g., <script src="jquery-3.6.0.slim.min.js">)
- When standalone=True: All assets are embedded inline (JS/CSS as text, images as base64)

The Jinja2 template uses conditionals:

html
{% if standalone %}
<script>{{ jquery_js }}</script>
<style>{{ bootstrap_css }}</style>
{% else %}
<script src="jquery-3.6.0.slim.min.js"></script>
<link href="bootstrap.min.css" rel="stylesheet">
{% endif %}

Module Imports

When importing submodules, be explicit:

python

Correct - mypy can verify this


import importlib.util
importlib.util.find_spec(mod_name)

Wrong - mypy error: Module has no attribute "util"


import importlib
importlib.util.find_spec(mod_name)

Testing

Test Files (tests/)

- test_coverup_*.py - Auto-generated coverage tests
- test_runningstats.py - Statistics tests (requires hypothesis)
- test_scalene_json.py - JSON output tests (requires hypothesis)
- test_nested_package_relative_import.py - Import handling tests

Test Dependencies

bash
pip install pytest pytest-asyncio hypothesis

Running Tests Across Python Versions

bash
for v in 3.9 3.10 3.11 3.12 3.13 3.14; do
python$v -m pytest tests/test_coverup_83.py -v
done

Flaky Smoketests

The smoketests in test/ can be flaky due to timing/sampling issues inherent to profiling:

- "No non-zero lines in X" - The profiler didn't collect enough samples. This happens when the test runs too quickly or signal delivery timing varies.
- "Expected function 'X' not returned" - A function wasn't sampled. Common with short-running functions.

These failures are usually timing-related and pass on re-run. They're more common on CI due to variable machine load.

Port Binding in Tests

When testing port availability, never use hardcoded ports - they may already be in use on CI runners:

python

Bad - port 49200 might be in use


port = 49200
sock.bind(("", port))

Good - find an available port first


port = find_available_port(49200, 49300)
if port is None:
return # Skip test if no ports available
sock.bind(("", port))

CI/CD (.github/workflows/)

- run-linters.yml - Runs mypy and ruff on Python 3.9-3.14
- tests.yml - Runs pytest on Python 3.9-3.14
- build-and-upload.yml - Build and publish to PyPI

Common Tasks

Adding a New CLI Option

1. Add default value in scalene_arguments.py:

python
class ScaleneArgumentsDict(TypedDict, total=False):
my_option: bool

2. Add argument in scalene_parseargs.py:

python
parser.add_argument(
"--my-option",
dest="my_option",
action="store_true",
default=defaults.my_option,
help="Description of option",
)

Adding a New AI Provider

1. Create provider module (scalene/scalene-gui/newprovider.ts):

typescript
export async function sendPromptToNewProvider(
prompt: string,
apiKey: string
): Promise<string> {
// API call implementation
}

export async function fetchNewProviderModels(apiKey: string): Promise<string[]> {
// Optional: fetch available models from API
}

2. Update optimizations.ts:
- Import the new module
- Add case in sendPromptToService() switch statement

3. Update index.html.template:
- Add option to #service select dropdown
- Add provider section with API key input, model selector, etc.
- Add CSS for .newprovider-fields visibility

4. Update scalene-gui.ts:
- Add provider to toggleServiceFields() function
- Add refresh handler if dynamic model fetching is supported
- Update getDefaultProvider() if env var support is needed

5. Update persistence.ts (for env var support):
- Add mapping in envKeyMap for new fields

6. Update scalene_utility.py:
- Read environment variable in api_keys dict
- Pass to template rendering

7. Rebuild the bundle:

bash
cd scalene/scalene-gui
npx esbuild scalene-gui.ts --bundle --outfile=scalene-gui-bundle.js --format=iife --global-name=ScaleneGUI

Environment Variable API Keys

The GUI supports prepopulating API keys from environment variables:

| Element ID | Environment Variable | Provider |
|------------|---------------------|----------|
| api-key | OPENAI_API_KEY | OpenAI |
| anthropic-api-key | ANTHROPIC_API_KEY | Anthropic |
| gemini-api-key | GEMINI_API_KEY or GOOGLE_API_KEY | Gemini |
| azure-api-key | AZURE_OPENAI_API_KEY | Azure OpenAI |
| azure-api-url | AZURE_OPENAI_ENDPOINT | Azure OpenAI |
| aws-access-key | AWS_ACCESS_KEY_ID | Amazon Bedrock |
| aws-secret-key | AWS_SECRET_ACCESS_KEY | Amazon Bedrock |
| aws-region | AWS_DEFAULT_REGION or AWS_REGION | Amazon Bedrock |

Flow:
1. scalene_utility.py reads env vars and passes to Jinja2 template
2. Template injects envApiKeys JavaScript object into page
3. persistence.ts uses env vars as fallbacks when localStorage is empty

Updating Version

Edit scalene/scalene_config.py:

python
scalene_version = "X.Y.Z"
scalene_date = "YYYY.MM.DD"

Dependencies

Key runtime dependencies:
- rich - Terminal formatting and colors
- cloudpickle - Serialization
- pynvml - NVIDIA GPU support (optional)

See requirements.txt for full list.

CLI Structure

Scalene uses a verb-based CLI with two main subcommands:

bash

Profile a program (saves to scalene-profile.json by default)


scalene run [options] yourprogram.py

View an existing profile


scalene view [options] [profile.json]

Run Subcommand Options

bash
scalene run prog.py                      # profile, save to scalene-profile.json
scalene run -o my.json prog.py # save to custom file
scalene run --cpu-only prog.py # profile CPU only (faster)
scalene run -c config.yaml prog.py # load options from config file
scalene run prog.py --- --arg # pass args to program

View Subcommand Options

bash
scalene view                             # open in browser
scalene view --cli # view in terminal
scalene view --html # save to scalene-profile.html
scalene view --standalone # save as self-contained HTML (all assets embedded)
scalene view myprofile.json # open specific profile

Profile Completion Message

After profiling completes, Scalene prints instructions for viewing the profile:

text
Scalene: profile saved to scalene-profile.json
To view in browser: scalene view
To view in terminal: scalene view --cli

The filename is only included in the command if a non-default output file was used.

YAML Configuration

Create a scalene.yaml file with options:

yaml
outfile: my-profile.json
cpu-only: true
profile-only: "mypackage,utils"
cpu-percent-threshold: 5

Load with: scalene run -c scalene.yaml prog.py

Advanced Options

Use scalene run --help-advanced to see all options including:
- --profile-all - profile all code, not just the target program
- --profile-only PATH - only profile files containing these strings
- --profile-exclude PATH - exclude files containing these strings
- --profile-system-libraries - profile Python stdlib and installed packages (skipped by default)
- --gpu - profile GPU time and memory
- --memory - profile memory usage
- --stacks - collect stack traces
- --profile-interval N - output profiles every N seconds

Smoke Tests

Smoke tests in test/ use the new CLI syntax:

python

test/smoketest.py


cmd = [sys.executable, "-m", "scalene", "run", "-o", str(outfile), *rest, fname]

GitHub Workflows

Workflows in .github/workflows/ use the new CLI:

yaml

Profile with interval, then view


- run: python -m scalene run --profile-interval=2 test/testme.py && python -m scalene view --cli

Profile with module invocation


- run: python -m scalene run --- -m import_stress_test && python -m scalene view --cli

Signal Handling

Scalene uses several Unix signals for profiling. The signal assignments are in scalene_signals.py:

| Signal | Purpose | Platform |
|--------|---------|----------|
| SIGVTALRM | CPU profiling timer (default) | Unix |
| SIGALRM | CPU profiling timer (real time mode) | Unix |
| SIGILL | Start profiling (--on) | Unix |
| SIGBUS | Stop profiling (--off) | Unix |
| SIGPROF | memcpy tracking | Unix |
| SIGXCPU | malloc tracking | Unix |
| SIGXFSZ | free tracking | Unix |

Signal Conflicts with Libraries

Libraries like PyTorch Lightning may also use these signals. The replacement_signal_fns.py module handles conflicts:

On Linux: Uses real-time signals (SIGRTMIN+1 to SIGRTMIN+5) for redirection. When user code sets a handler for a Scalene signal, their handler is redirected to a real-time signal. Calls to raise_signal() and kill() are also redirected transparently.

On macOS/other platforms: Uses handler chaining. Both Scalene's handler and the user's handler are called when the signal fires.

python

Platform-specific signal handling


_use_rt_signals = sys.platform == "linux" and hasattr(signal, "SIGRTMIN")

if _use_rt_signals:
# Linux: redirect to real-time signals
rt_base = signal.SIGRTMIN + 1
_signal_redirects[signal.SIGILL] = rt_base
else:
# macOS: chain handlers
def chained_handler(sig, frame):
scalene_handler(sig, frame)
user_handler(sig, frame)

Frame Line Number Can Be None (Python 3.11+)

In Python 3.11+, frame.f_lineno can be None in edge cases (e.g., during multiprocessing cleanup). Always use a fallback:

python
lineno = frame.f_lineno if frame.f_lineno is not None else frame.f_code.co_firstlineno

Native Extension Build Issues

C++ Standard Library Conflicts with vendor/printf

The vendor/printf/printf.h header defines macros that conflict with C++ standard library:

c
#define vsnprintf vsnprintf_
#define snprintf snprintf_

This breaks std::vsnprintf in <string> and other headers. Fix: Include C++ standard headers BEFORE vendor headers in src/source/libscalene.cpp:

cpp
// Include C++ standard headers FIRST
#include <cstddef>
#include <string>

// Then vendor headers that define conflicting macros
#include <heaplayers.h> // Eventually includes printf.h

Profiling Guide

See Scalene-Agents.md for detailed information about interpreting Scalene's profiling output, including Python vs C time, memory metrics, and optimization strategies.

Debugging Guide

See Scalene-Debugging.md for signal handler debugging, async profiling debugging, the profile output pipeline (three separate renderers!), and unbounded growth prevention patterns.

GUI Development Guide

See Scalene-GUI.md for adding new columns, Vega-Lite chart types, pie chart best practices (two-wedge rendering, rotating pies), and the chart rendering flow.


README.md

!scalene

Scalene: a Python CPU+GPU+memory profiler with AI-powered optimization proposals

by Emery Berger, Sam Stern, and Juan Altmayer Pizzorno.

![Scalene community Slack](https://join.slack.com/t/scaleneprofil-jge3234/shared_invite/zt-110vzrdck-xJh5d4gHnp5vKXIjYD3Uwg)Scalene community Slack

![PyPI Latest Release](https://pypi.org/project/scalene/)![Anaconda-Server Badge](https://anaconda.org/conda-forge/scalene) ![Downloads](https://pepy.tech/project/scalene)![Anaconda downloads](https://anaconda.org/conda-forge/scalene) ![Downloads](https://pepy.tech/project/scalene) !Python versions![Visual Studio Code Extension version](https://marketplace.visualstudio.com/items?itemName=EmeryBerger.scalene) !License ![GitHub Repo stars](https://github.com/plasma-umass/scalene)


!Ozsvald tweet

(tweet from Ian Ozsvald, author of _High Performance Python_)

!Semantic Scholar success story

_Python Profiler Links to AI to Improve Code Scalene identifies inefficiencies and asks GPT-4 for suggestions_, IEEE Spectrum

Episode 172: Measuring Multiple Facets of Python Performance With Scalene, The Real Python podcast

Scalene web-based user interface: https://scalene-gui.github.io/scalene-gui/

About Scalene

Scalene is a high-performance CPU, GPU and memory profiler for
Python that does a number of things that other Python profilers do not
and cannot do. It runs orders of magnitude faster than many other
profilers while delivering far more detailed information. It is also
the first profiler ever to incorporate AI-powered proposed
optimizations.

AI-powered optimization suggestions

Note

> For optimization suggestions, Scalene supports a variety of AI providers, including Amazon Bedrock, Microsoft Azure, OpenAI, and local models via Ollama. To enable AI-powered optimization suggestions from AI providers, you need to select a provider and, if needed, enter your credentials, in the box under "AI Optimization Options".

> <img width="607" height="316" alt="AI Optimization Options" src="https://github.com/user-attachments/assets/3c803237-063f-481a-8624-5c1d7f205c8a" />


Once you've entered your key and any other needed data, click on the lightning bolt (⚡) beside any line or the explosion (💥) for an entire region of code to generate a proposed optimization. Click on a proposed optimization to copy it to the clipboard.

<img width="571" alt="example proposed optimization" src="https://user-images.githubusercontent.com/1612723/211639968-37cf793f-3290-43d1-9282-79e579558388.png">

You can click as many times as you like on the lightning bolt or explosion, and it will generate different suggested optimizations. Your mileage may vary, but in some cases, the suggestions are quite impressive (e.g., order-of-magnitude improvements).

Quick Start

#### Installing Scalene:

console
python3 -m pip install -U scalene

or

console
conda install -c conda-forge scalene

#### Using Scalene:

After installing Scalene, you can use Scalene at the command line, or as a Visual Studio Code extension.

<details>
<summary>
Using the Scalene VS Code Extension:
</summary>

First, install <a href="https://marketplace.visualstudio.com/items?itemName=EmeryBerger.scalene">the Scalene extension from the VS Code Marketplace</a> or by searching for it within VS Code by typing Command-Shift-X (Mac) or Ctrl-Shift-X (Windows). Once that's installed, click Command-Shift-P or Ctrl-Shift-P to open the <a href="https://code.visualstudio.com/docs/getstarted/userinterface">Command Palette</a>. Then select <b>"Scalene: AI-powered profiling..."</b> (you can start typing Scalene and it will pop up if it's installed). Run that and, assuming your code runs for at least a second, a Scalene profile will appear in a webview.

<img width="734" alt="Screenshot 2023-09-20 at 7 09 06 PM" src="https://github.com/plasma-umass/scalene/assets/1612723/7e78e3d2-e649-4f02-86fd-0da2a259a1a4">

</details>

<details>
<summary>
Commonly used command-line options:
</summary>

Scalene uses a verb-based command structure with two main commands: run (to profile) and view (to display results).

console

Profile a program (saves to scalene-profile.json)


scalene run your_prog.py
python3 -m scalene run your_prog.py # equivalent alternative

View a profile


scalene view # open profile in browser
scalene view --cli # view in terminal
scalene view --html # save to scalene-profile.html
scalene view --standalone # save as self-contained HTML

Common profiling options


scalene run --cpu-only your_prog.py # only profile CPU (faster)
scalene run -o results.json your_prog.py # custom output filename
scalene run -c config.yaml your_prog.py # load options from config file

Pass arguments to your program (use --- separator)


scalene run your_prog.py --- --arg1 --arg2

Get help


scalene --help # main help
scalene run --help # profiling options
scalene run --help-advanced # advanced profiling options
scalene view --help # viewing options

</details>

<details>
<summary>
Using a YAML configuration file:
</summary>

You can store Scalene options in a YAML configuration file and load them with -c or --config:

console
scalene run -c scalene.yaml your_prog.py

Example scalene.yaml:

yaml

Output options


outfile: my-profile.json

Profiling mode (use only one)


cpu-only: true # CPU profiling only (faster)

gpu: true # Include GPU profiling


memory: true # Include memory profiling

Filter what gets profiled


profile-only: "mypackage,mymodule" # Only profile these paths
profile-exclude: "tests,venv" # Exclude these paths
profile-all: false # Profile all code, not just target

Performance tuning


cpu-percent-threshold: 1 # Min CPU% to report (default: 1)
cpu-sampling-rate: 0.01 # Sampling interval in seconds
malloc-threshold: 100 # Min allocations to report

Other options


use-virtual-time: false # Measure CPU time only (not I/O)
stacks: false # Collect stack traces
memory-leak-detector: true # Detect likely memory leaks

Command-line arguments override config file settings.

</details>

<details>
<summary>
Using Scalene programmatically in your code:
</summary>

Invoke using scalene as above and then:

Python
from scalene import scalene_profiler

Turn profiling on


scalene_profiler.start()

your code

Turn profiling off


scalene_profiler.stop()

Python
from scalene.scalene_profiler import enable_profiling

with enable_profiling():
# do something

</details>

<details>
<summary>
Using Scalene to profile only specific functions via <code>@profile</code>:
</summary>

Just preface any functions you want to profile with the @profile decorator and run it with Scalene:

Python

do not import profile!

@profile
def slow_function():
import time
time.sleep(3)

</details>

#### Web-based GUI

Scalene has both a CLI and a web-based GUI (demo here).

By default, once Scalene has profiled your program, it will open a
tab in a web browser with an interactive user interface (all processing is done
locally). Hover over bars to see breakdowns of CPU and memory
consumption, and click on underlined column headers to sort the
columns. The GUI works fully offline with no internet connection required.

Use scalene view --standalone to generate a completely self-contained HTML file with all assets embedded, perfect for sharing or archiving.

![Scalene web GUI](https://raw.githubusercontent.com/plasma-umass/scalene/master/docs/scalene-gui-example-full.png)


Scalene Overview

Scalene talk (PyCon US 2021)

This talk presented at PyCon 2021 walks through Scalene's advantages and how to use it to debug the performance of an application (and provides some technical details on its internals). We highly recommend watching this video!

![Scalene presentation at PyCon 2021](https://youtu.be/5iEf-_7mM1k "Scalene presentation at PyCon 2021")

Fast and Accurate

- Scalene is _fast_. It uses sampling instead of instrumentation or relying on Python's tracing facilities. Its overhead is typically no more than 10-20% (and often less).

- Scalene is accurate. We tested CPU profiler accuracy and found that Scalene is among the most accurate profilers, correctly measuring time taken.

!Profiler accuracy

- Scalene performs profiling _at the line level_ _and_ _per function_, pointing to the functions and the specific lines of code responsible for the execution time in your program.

CPU profiling

- Scalene separates out time spent in Python from time in native code (including libraries). Most Python programmers aren't going to optimize the performance of native code (which is usually either in the Python implementation or external libraries), so this helps developers focus their optimization efforts on the code they can actually improve.
- Scalene highlights hotspots (code accounting for significant percentages of CPU time or memory allocation) in red, making them even easier to spot.
- Scalene also separates out system time, making it easy to find I/O bottlenecks.

GPU profiling

- Scalene reports GPU time (currently limited to NVIDIA-based systems).

Memory profiling

- Scalene profiles memory usage. In addition to tracking CPU usage, Scalene also points to the specific lines of code responsible for memory growth. It accomplishes this via an included specialized memory allocator.
- Scalene separates out the percentage of memory consumed by Python code vs. native code.
- Scalene produces _per-line_ memory profiles.
- Scalene identifies lines with likely memory leaks.
- Scalene profiles _copying volume_, making it easy to spot inadvertent copying, especially due to crossing Python/library boundaries (e.g., accidentally converting numpy arrays into Python arrays, and vice versa).

Other features

- Scalene can produce reduced profiles (via --reduced-profile) that only report lines that consume more than 1% of CPU or perform at least 100 allocations.
- Scalene supports @profile decorators to profile only specific functions.
- When Scalene is profiling a program launched in the background (via &), you can suspend and resume profiling.

Comparison to Other Profilers

Performance and Features

Below is a table comparing the performance and features of various profilers to Scalene.

!Performance and feature comparison

- Slowdown: the slowdown when running a benchmark from the Pyperformance suite. Green means less than 2x overhead. Scalene's overhead is just a 35% slowdown.

Scalene has all of the following features, many of which only Scalene supports:

- Lines or functions: does the profiler report information only for entire functions, or for every line -- Scalene does both.
- Unmodified Code: works on unmodified code.
- Threads: supports Python threads.
- Multiprocessing: supports use of the multiprocessing library -- _Scalene only_
- Python vs. C time: breaks out time spent in Python vs. native code (e.g., libraries) -- _Scalene only_
- System time: breaks out system time (e.g., sleeping or performing I/O) -- _Scalene only_
- Profiles memory: reports memory consumption per line / function
- GPU: reports time spent on an NVIDIA GPU (if present) -- _Scalene only_
- Memory trends: reports memory use over time per line / function -- _Scalene only_
- Copy volume: reports megabytes being copied per second -- _Scalene only_
- Detects leaks: automatically pinpoints lines responsible for likely memory leaks -- _Scalene only_

Output

If you include the --cli option, Scalene prints annotated source code for the program being profiled
(as text, JSON (--json), or HTML (--html)) and any modules it
uses in the same directory or subdirectories (you can optionally have
it --profile-all and only include files with at least a
--cpu-percent-threshold of time). Here is a snippet from
pystone.py.

!Example profile

* Memory usage at the top: Visualized by "sparklines", memory consumption over the runtime of the profiled code.
* "Time Python": How much time was spent in Python code.
* "native": How much time was spent in non-Python code (e.g., libraries written in C/C++).
* "system": How much time was spent in the system (e.g., I/O).
* "GPU": (not shown here) How much time spent on the GPU, if your system has an NVIDIA GPU installed.
* "Memory Python": How much of the memory allocation happened on the Python side of the code, as opposed to in non-Python code (e.g., libraries written in C/C++).
* "net": Positive net memory numbers indicate total memory allocation in megabytes; negative net memory numbers indicate memory reclamation.
* "timeline / %": Visualized by "sparklines", memory consumption generated by this line over the program runtime, and the percentages of total memory activity this line represents.
* "Copy (MB/s)": The amount of megabytes being copied per second (see "About Scalene").

Scalene

The following command runs Scalene on a provided example program.

console
scalene test/testme.py

<details>
<summary>
Click to see all Scalene's options (available by running with <code>--help</code>)
</summary>

console
% scalene --help
Scalene: a high-precision CPU and memory profiler, version 1.5.51 (2025.01.29)
https://github.com/plasma-umass/scalene

commands:
run Profile a Python program (saves to scalene-profile.json)
view View an existing profile in browser or terminal

examples:
% scalene run your_program.py # profile, save to scalene-profile.json
% scalene view # view scalene-profile.json in browser
% scalene view --cli # view profile in terminal

in Jupyter, line mode:
%scrun [options] statement

in Jupyter, cell mode:
%%scalene [options]
your code here

% scalene run --help
Profile a Python program with Scalene.

examples:
% scalene run prog.py # profile, save to scalene-profile.json
% scalene run -o my.json prog.py # save to custom file
% scalene run --cpu-only prog.py # profile CPU only (faster)
% scalene run -c scalene.yaml prog.py # load options from config file
% scalene run prog.py --- --arg # pass args to program
% scalene run --help-advanced # show advanced options

options:
-h, --help show this help message and exit
-o, --outfile OUTFILE output file (default: scalene-profile.json)
--cpu-only only profile CPU time (no memory/GPU)
-c, --config FILE load options from YAML config file
--help-advanced show advanced options

% scalene run --help-advanced
Advanced options for scalene run:

background profiling:
Use --off to start with profiling disabled, then control it from another terminal:
% scalene run --off prog.py # start with profiling off
% python3 -m scalene.profile --on --pid <PID> # resume profiling
% python3 -m scalene.profile --off --pid <PID> # suspend profiling

options:
--profile-all profile all code, not just the target program
--profile-only PATH only profile files containing these strings (comma-separated)
--profile-exclude PATH exclude files containing these strings (comma-separated)
--profile-system-libraries profile Python stdlib and installed packages (default: skip)
--gpu profile GPU time and memory
--memory profile memory usage
--stacks collect stack traces
--profile-interval N output profiles every N seconds (default: inf)
--use-virtual-time measure only CPU time, not I/O or blocking
--cpu-percent-threshold N only report lines with at least N% CPU (default: 1%)
--cpu-sampling-rate N CPU sampling rate in seconds (default: 0.01)
--allocation-sampling-window N allocation sampling window in bytes
--malloc-threshold N only report lines with at least N allocations (default: 100)
--program-path PATH directory containing code to profile
--memory-leak-detector EXPERIMENTAL: report likely memory leaks
--on start with profiling on (default)
--off start with profiling off

% scalene view --help
View an existing Scalene profile.

examples:
% scalene view # open in browser
% scalene view --cli # view in terminal
% scalene view --html # save to scalene-profile.html
% scalene view --standalone # save as self-contained HTML
% scalene view myprofile.json # open specific profile in browser

options:
-h, --help show this help message and exit
--cli display profile in the terminal
--html save to scalene-profile.html (no browser)
--standalone save as self-contained HTML with all assets embedded
-r, --reduced only show lines with activity (--cli mode)


</details>

Scalene with Jupyter

<details>
<summary>
Instructions for installing and using Scalene with Jupyter notebooks
</summary>

This notebook illustrates the use of Scalene in Jupyter.

Installation:

console
!pip install scalene
%load_ext scalene

Line mode:

console
%scrun [options] statement

Cell mode:

console
%%scalene [options]
code...
code...

</details>

Installation

<details open>
<summary>Using <code>pip</code> (Mac OS X, Linux, Windows, and WSL2)</summary>

Scalene is distributed as a pip package and works on Mac OS X, Linux (including Ubuntu in Windows WSL2) and Windows platforms.

Note for Windows users

> Starting with Scalene 2.0, Windows supports full memory profiling. If you

encounter issues, ensure you have the Visual C++ Redistributable

installed. If building from source, you will need Visual C++ Build Tools and CMake.

You can install it as follows:

console
% pip install -U scalene

or

console
% python3 -m pip install -U scalene

You may need to install some packages first.

See https://stackoverflow.com/a/19344978/4954434 for full instructions for all Linux flavors.

For Ubuntu/Debian:

console
% sudo apt install git python3-all-dev

</details>

<details>
<summary>Using <code>conda</code> (Mac OS X, Linux, Windows, and WSL2)</summary>

console
% conda install -c conda-forge scalene

Scalene is distributed as a conda package and works on Mac OS X, Linux (including Ubuntu in Windows WSL2) and Windows platforms.

Note for Windows users

> Starting with Scalene 2.0, Windows supports full memory profiling. If you

encounter issues, ensure you have the Visual C++ Redistributable

installed.

</details>

<details>
<summary>On ArchLinux</summary>

You can install Scalene on Arch Linux via the AUR
package
. Use your favorite AUR helper, or
manually download the PKGBUILD and run makepkg -cirs to build. Note that this will place
libscalene.so in /usr/lib; modify the below usage instructions accordingly.
</details>

Frequently Asked Questions

<details>
<summary>
Can I use Scalene with PyTest?
</summary>

A: Yes! You can run it as follows (for example):

scalene run -m pytest your_test.py

or

python3 -m scalene run -m pytest your_test.py

</details>

<details>
<summary>
Is there any way to get shorter profiles or do more targeted profiling?
</summary>

A: Yes! There are several options:

1. Use --reduced-profile to include only lines and files with memory/CPU/GPU activity.
2. Use --profile-only to include only filenames containing specific strings (as in, --profile-only foo,bar,baz).
3. Decorate functions of interest with @profile to have Scalene report _only_ those functions.
4. Turn profiling on and off programmatically by importing Scalene profiler (from scalene import scalene_profiler) and then turning profiling on and off via scalene_profiler.start() and scalene_profiler.stop(). By default, Scalene runs with profiling on, so to delay profiling until desired, use the --off command-line option (scalene run --off yourprogram.py).
</details>

<details>
<summary>
How do I run Scalene in PyCharm?
</summary>

A: In PyCharm, you can run Scalene at the command line by opening the terminal at the bottom of the IDE and running a Scalene command (e.g., scalene run <your program>). Then use scalene view --html to generate an HTML file (scalene-profile.html) that you can view in the IDE.
</details>

<details>
<summary>
How do I use Scalene with Django?
</summary>

A: Pass in the --noreload option (see https://github.com/plasma-umass/scalene/issues/178).
</details>


<details>
<summary>
Does Scalene work with gevent/Greenlets?
</summary>

A: Yes! Put the following code in the beginning of your program, or modify the call to monkey.patch_all as below:

python
from gevent import monkey
monkey.patch_all(thread=False)

</details>

<details>
<summary>
How do I use Scalene with PyTorch on the Mac?
</summary>

A: Scalene works with PyTorch version 1.5.1 on Mac OS X. There's a bug in newer versions of PyTorch (https://github.com/pytorch/pytorch/issues/57185) that interferes with Scalene (discussion here: https://github.com/plasma-umass/scalene/issues/110), but only on Macs.
</details>

Technical Information

For details about how Scalene works, please see the following paper, which won the Jay Lepreau Best Paper Award at OSDI 2023: Triangulating Python Performance Issues with Scalene. (Note that this paper does not include information about the AI-driven proposed optimizations.)

<details>
<summary>
To cite Scalene in an academic paper, please use the following:
</summary>

latex
@inproceedings{288540,
author = {Emery D. Berger and Sam Stern and Juan Altmayer Pizzorno},
title = {Triangulating Python Performance Issues with {S}calene},
booktitle = {{17th USENIX Symposium on Operating Systems Design and Implementation (OSDI 23)}},
year = {2023},
isbn = {978-1-939133-34-2},
address = {Boston, MA},
pages = {51--64},
url = {https://www.usenix.org/conference/osdi23/presentation/berger},
publisher = {USENIX Association},
month = jul
}

</details>


Success Stories

If you use Scalene to successfully debug a performance problem, please add a comment to this issue!


Acknowledgements

Logo created by Sophia Berger.

This material is based upon work supported by the National Science
Foundation under Grant No. 1955610. Any opinions, findings, and
conclusions or recommendations expressed in this material are those of
the author(s) and do not necessarily reflect the views of the National
Science Foundation.