Shared Profiling Module
Shared profiling and monitoring infrastructure for Ray Data benchmarks.
Used by benchmarks under release/nightly_tests/dataset/ (e.g.
image_embedding_from_jsonl).
How to instrument a benchmark
Step 1: Build an image with profiling tools
build_incremental_ray.sh must be invoked from a Ray or Rayturbo source
root (it shells out to bazel, looks for a python/ray subdirectory,
and pushes the resulting image to ECR).
cd ~/work/anyscale/rayturbo2 # or wherever your source tree is
release/nightly_tests/dataset/profiling/build/build_incremental_ray.sh \
--tag <your-tag> --extra
--extra adds nsys, perf, gdb, and the cloud SDKs to the base image.
See profiling/build/README.md for --ml, --python-only, and other
build modes.
Step 2: Add profiling to your benchmark's main.py
from profiling.coordinator import Profiling
from profiling import nvtx as profiling_nvtx
# Create a Profiling instance with your output directory and GPU node count.
# All configuration is read from environment variables (see table below).
profiling = Profiling(
outdir="/mnt/shared_storage/my_benchmark/<job_id>",
num_gpu_nodes=40,
)
# Start all enabled profilers and monitors.
profiling.start()
# Get nsys runtime_env for GPU workers (returns {} if PROFILER_MODE != "nsys").
infer_kwargs["runtime_env"] = profiling.nsys_runtime_env()
# Add NVTX annotations to your GPU actor's __call__ method:
class MyActor:
def __call__(self, batch):
with profiling_nvtx.profiling_range("my_operation"):
...
# After the benchmark completes, stop profilers and upload telemetry.
profiling.stop(s3_prefix="my-benchmark/<job_id>")
That's it. The Profiling class handles starting/stopping py-spy, perf,
nvidia-smi, and network monitors based on which env vars are set.
Step 3: Configure env vars in job.yaml
image_uri: 830883877497.dkr.ecr.us-west-2.amazonaws.com/anyscale/ray:<your-tag>
working_dir: .
env_vars:
PROFILER_MODE: "nsys" # "nsys", "torch", or "none"
PYSPY_ENABLED: "1" # Enable py-spy CPU profiling
PERF_PROFILING_ENABLED: "1" # Enable perf record on raylet/gcs
Step 4: Submit the job from the dataset/ directory
cd release/nightly_tests/dataset
anyscale jobs submit -f my_benchmark/job.yaml
Submitting from dataset/ ensures benchmark.py and profiling/ are
in scope as the working directory.
Step 5: Download results and analyze
./profiling/analysis/download_job_output.sh prodjob_abc123 my-benchmark/prodjob_abc123
cd prodjob_abc123
../profiling/analysis/analyze_pyspy_profile.py pyspy_driver.speedscope.json --list-threads
# Worker-node UDF profiles (one per sampled ray:: worker):
../profiling/analysis/analyze_pyspy_profile.py pyspy_worker_<nodeip>_Infer.speedscope.json --list-threads
../profiling/analysis/analyze_perf_profiles.sh
See analysis/README.md for more analysis examples.
Environment variables
| Variable | Default | Description |
|---|---|---|
PROFILER_MODE |
"none" |
GPU profiler: "nsys", "torch", or "none" |
PROFILE_SKIP_BATCHES |
0 |
Batches to skip before nsys capture starts |
PROFILE_ACTIVE_BATCHES |
10000 |
Upper bound on captured batches per actor; left high so atexit closes the capture range |
PYSPY_ENABLED |
"0" |
Set to "1" to enable py-spy CPU profiling |
PYSPY_NUM_CPU_WORKERS |
5 |
Number of CPU worker nodes to py-spy (0 to disable worker py-spy) |
PYSPY_NUM_GPU_WORKERS |
5 |
Number of GPU worker nodes to py-spy (0 to disable worker py-spy) |
PERF_PROFILING_ENABLED |
"0" |
Set to "1" to enable perf record on raylet/gcs |
PERF_NUM_CPU_WORKERS |
5 |
Number of CPU worker nodes to perf profile |
PERF_NUM_GPU_WORKERS |
5 |
Number of GPU worker nodes to perf profile |
GPU_MONITOR_ENABLED |
"0" |
Set to "1" to enable nvidia-smi monitoring |
NET_MONITOR_ENABLED |
"0" |
Set to "1" to enable network I/O monitoring |
OBJECT_STORE_MONITOR_ENABLED |
"0" |
Set to "1" to enable per-(node, operator) object-store sampling |
OBJECT_STORE_MONITOR_INTERVAL_S |
5 |
Seconds between samples after the fast window |
OBJECT_STORE_MONITOR_FAST_WINDOW_S |
60 |
Seconds at the start of the run to sample at the fast interval (set 0 to disable) |
OBJECT_STORE_MONITOR_FAST_INTERVAL_S |
1 |
Tick interval during the fast window |
RAY_MAX_LIMIT_FROM_API_SERVER |
10000 |
Object-store sampling caps list_objects() at 10k by default; set to e.g. 200000 on the head node to fully sample large runs |
PROFILING_S3_BUCKET |
anyscale-staging-data-cld-kvedzwag2qa8i5bjxuevf5i7 |
S3 bucket for telemetry upload |
Modules
| Module | What it does |
|---|---|
coordinator.py |
Orchestrates all profilers — the main entry point for benchmarks |
pyspy.py |
Attaches py-spy to the driver process and to Ray UDF workers on sampled worker nodes |
perf.py |
Runs perf record on GCS/raylet C++ processes (head + worker nodes) |
gpu_monitor.py |
Launches nvidia-smi dmon on GPU nodes as they join the cluster |
net_monitor.py |
Samples psutil.net_io_counters on every node, writes CSV |
nsys.py |
Builds runtime_env config to wrap Ray workers with nsys profile |
nvtx.py |
NVTX range annotations and CUDA profiler start/stop for nsys capture control |
object_store.py |
Samples Ray's state API for per-(node, operator) primary-byte time series; writes object_store_state.csv, actor_placement.csv, and plasma_stats.csv |
telemetry.py |
Uploads profiling artifacts from shared storage to S3 |
Subdirectories
Example
See image_embedding_from_jsonl/main.py for a complete working example.