# Tachyon (Python 3.15 Sampling Profiler) — Usage Reference

Extracted from pip optimization sessions where Tachyon was used successfully.

## Why Tachyon

- **Zero overhead**: External sampling reads process memory without instrumenting the target
- **Production-safe**: No code changes needed, can attach to running processes
- **Accurate**: Statistical profiler — results are proportional to real time, unlike cProfile which adds 30x overhead
- **Built-in**: Ships with Python 3.15, no pip install needed

## Requirements

- Python 3.15+ (both profiler and target must use the same minor version)
- **Linux**: `sudo` or `CAP_SYS_PTRACE`
- **macOS**: `sudo` or debugger entitlement

## Commands That Worked (from pip sessions)

### Basic CPU profiling
```bash
sudo -E -H python3.15 -m profiling.sampling run \
  --mode cpu --sort tottime --limit 40 \
  /tmp/workload.py
```

### Focused micro-workload
```bash
sudo -A -E -H python3.15 -m profiling.sampling run \
  --mode cpu --sort tottime --limit 30 \
  /tmp/workload_specfilter.py
```

### Flamegraph (visual)
```bash
sudo -E python3.15 -m profiling.sampling run \
  --mode cpu --flamegraph --browser \
  /tmp/bench_quick.py
```

### Heatmap (line-level)
```bash
sudo -E python3.15 -m profiling.sampling run \
  --mode cpu --heatmap -o /tmp/heatmap \
  /tmp/workload.py
```

### Higher sample rate
```bash
sudo -E python3.15 -m profiling.sampling run \
  --mode cpu -r 10khz --sort tottime --limit 40 \
  /tmp/workload.py
```

### Differential flamegraph (A/B comparison)
```bash
# Baseline
python3.15 -m profiling.sampling run --binary -o baseline.bin workload.py

# Optimized — shows diff
python3.15 -m profiling.sampling run --diff-flamegraph baseline.bin -o diff.html workload.py
```

## Key Flags

| Flag | Purpose |
|---|---|
| `--mode cpu` | Only CPU time (ignores I/O waits) — use for optimization work |
| `--mode wall` | Total elapsed time (default) — use for user-perceived latency |
| `--mode gil` | GIL contention — use for threaded code |
| `--sort tottime` | Sort by time spent directly in function |
| `--sort cumtime` | Sort by cumulative time including callees |
| `--limit N` | Show top N entries |
| `-r 10khz` | 10,000 samples/sec (more precision for short functions) |
| `--flamegraph` | Interactive HTML visualization |
| `--heatmap` | Line-level HTML reports |
| `--binary -o file.bin` | Save for later replay/conversion |

## Output Format (pstats)

```
nsamples   sample%  tottime (ms)    cumul%   cumtime (s)  filename:lineno(function)
    567/567       3.7       567.000       3.7         0.567  tempfile.py:381(mkdtemp)
   461/8152       3.0       461.000      53.1         8.152  resolution.py:221(_attempt_to_pin_criterion)
```

- `nsamples`: direct/cumulative sample counts
- `sample%`: percentage of total samples when directly executing
- `tottime`: estimated wall time in this function only
- `cumul%`: percentage when function was anywhere on the call stack
- `cumtime`: cumulative time including all callees

## When Tachyon Can't Be Used

Tachyon requires the target code to run under the same Python 3.15 interpreter. This fails when:

1. **C extension dependencies** — packages with compiled C/Rust extensions (pydantic-core, libcst, lxml) don't have 3.15 wheels and may fail to build
2. **ABI incompatibility** — loading 3.13-compiled `.so` files from 3.15 via `sys.path` fails on C extensions

### Fallbacks

| Tool | Overhead | When to use |
|---|---|---|
| `cProfile` | ~30x | Exact call counts, short workloads, any Python version |
| `py-spy` | ~0x | Sampling profiler, any Python version, needs `sudo` |
| `-X importtime` | ~0x | Import chain analysis only |

## Workload Script Pattern

The key pattern: write a focused workload script that exercises the hot path, not the full CLI.

```python
# /tmp/workload_resolve.py
import sys
import os

# Setup: import the code under test
sys.path.insert(0, 'src')
from pip._internal.resolution.resolvelib.resolver import Resolver
from pip._internal.index.package_finder import PackageFinder

# Exercise: run the hot path N times
# (enough iterations for sampling to capture signal)
for _ in range(10):
    # ... resolve a complex dependency tree ...
```

This approach:
- Isolates the hot path from CLI startup noise
- Gives the sampler enough samples (1khz × seconds of runtime)
- Can be used with `--diff-flamegraph` for A/B comparison
