Write a complete latency-benchmarking script with warmup, synchronisation, percentiles, and memory tracking. Use when you need help with skill latency profiler.
Installs into .claude/skills of the current project.
Are you the author of Skill Latency Profiler?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/anubhavg-icpl-skill-latency-profiler)
---
name: skill-latency-profiler
description: Write a complete latency-benchmarking script with warmup, synchronisation, percentiles, and memory tracking. Use when you need help with skill latency profiler.
license: CC-BY-NC-SA-4.0
phase: 4
lesson: 15
metadata:
version: 1.0.0
tags: [edge, deployment, profiling, benchmarking]
---
# Latency Profiler
Produce a disciplined latency benchmark for any PyTorch model. Reports that anyone downstream can actually trust.
## When to use
- Comparing multiple candidate backbones before picking one to deploy.
- Before and after quantisation or pruning.
- After a runtime change (eager vs ONNX vs TensorRT).
- Generating a deployment-readiness report.
## Inputs
- `model`: PyTorch `nn.Module`.
- `input_shape`: tuple like `(1, 3, 224, 224)`.
- `device`: `cpu` | `cuda` | `mps`.
- `warmup`: default 10.
- `iters`: default 100.
## Checks
### 1. Warmup
Run the model `warmup` times without timing. Catches first-forward JIT compilation and cold cache effects.
### 2. Synchronisation
For `cuda`, call `torch.cuda.synchronize()` before and after each timed forward pass.
For `mps`, call `torch.mps.synchronize()`.
### 3. Timer
Use `time.perf_counter()` for wall-clock measurement. Convert to milliseconds.
### 4. Percentiles
Sort the full list of timings. Report `p50, p90, p95, p99, mean, std`.
### 5. Memory
For `cuda`, call `torch.cuda.max_memory_allocated()` after the run and subtract any baseline.
For `cpu`, use `tracemalloc` or `psutil.Process().memory_info().rss` before and after.
### 6. Batch-size sweep
Optionally repeat the benchmark for `batch_size in [1, 4, 16, 32]` to reveal throughput vs latency tradeoffs.
## Output template
```python
import time
import torch
import psutil, os
def profile(model, input_shape, device="cpu", warmup=10, iters=100):
proc = psutil.Process(os.getpid())
baseline_rss = proc.memory_info().rss / 1e6
model = model.to(device).eval()
x = torch.randn(input_shape, device=device)
def sync():
if device == "cuda":
torch.cuda.synchronize()
elif device == "mps":
torch.mps.synchronize()
with torch.no_grad():
for _ in range(warmup):
model(x)
sync()
if device == "cuda":
torch.cuda.reset_peak_memory_stats()
times = []
for _ in range(iters):
sync()
t0 = time.perf_counter()
model(x)
sync()
times.append((time.perf_counter() - t0) * 1000)
times.sort()
mean = sum(times) / len(times)
std = (sum((t - mean) ** 2 for t in times) / len(times)) ** 0.5
def pct(p):
idx = max(0, min(len(times) - 1, int(len(times) * p) - 1))
return times[idx]
report = {
"p50_ms": pct(0.50),
"p90_ms": pct(0.90),
"p95_ms": pct(0.95),
"p99_ms": pct(0.99),
"mean_ms": mean,
"std_ms": std,
"rss_mb": proc.memory_info().rss / 1e6 - baseline_rss,
}
if device == "cuda":
report["peak_cuda_mb"] = torch.cuda.max_memory_allocated() / 1e6
return report
```
## Rules
- Always run warmup; never trust a first-forward timing.
- Percentiles, not mean — a single outlier can double the mean but barely move p50.
- Use the same input_shape as production; latency on 224x224 is not latency on 384x384.
- For CUDA, never omit `torch.cuda.synchronize()`; the numbers are meaningless without it.
- Log the torch version, CUDA version, and device name alongside the numbers — they stop being comparable otherwise.