Detailed step-by-step flow diagrams for every major execution path in the project. Each diagram is paired with an explanation of the decision points and data transformations involved.
Shreedhar Kodate·26 March 2025·4 min read
ray-engineeringdocs
Detailed step-by-step flow diagrams for every major execution path in the
project. Each diagram is paired with an explanation of the decision points
and data transformations involved.
1. Task Execution Flow
How a single @ray.remote task travels from Python submission to result
retrieval.
flowchart TD
A([Driver calls\nfn.remote(args)]) --> B[Driver process\nserialises arguments\nvia Apache Arrow]
B --> C{Are args\nObjectRefs?}
C -->|Yes – zero-copy| D[Pass ObjectRef ID\nto scheduler]
C -->|No – raw data| E[Store args in\nlocal Plasma Store\nGet ObjectRef]
E --> D
D --> F[GCS Scheduler\nmatches task to\navailable worker slot]
F --> G{Worker on\nsame node?}
G -->|Yes| H[Read args from\nlocal shared memory\nzero-copy mmap]
G -->|No| I[Transfer object\nvia network to\nremote Plasma Store]
I --> H
H --> J[Worker process\nexecutes fn(args)]
J --> K[Serialise return value\nstore in local Plasma]
K --> L[Return ObjectRef\nto Driver]
L --> M([Driver calls\nray.get(ref)])
M --> N{Object on\nsame node?}
N -->|Yes| O[Read from local\nPlasma Store\nzero-copy]
N -->|No| P[Fetch object\nover network]
P --> O
O --> Q([Python object\nreturned to Driver])
Key performance observations
Arguments that are already ObjectRefs skip serialisation entirely.
For same-node communication, plasma shared memory avoids any copy.
ray.wait() lets you process results as they finish rather than blocking
on the slowest task.
2. Ray Data Preprocessing Pipeline Flow
flowchart TD
subgraph Input
RAW["Raw Data Source\n(CSV / Parquet / Pandas)"]
end
RAW --> CREATE["ray.data.from_pandas(df)\nor read_parquet(path)\nPartitioned into N Blocks"]
subgraph LazyTransforms["Lazy Transform Chain"]
CREATE --> T1[".map_batches(normalise_batch)\nbatch_format=numpy\nRun in parallel Ray tasks"]
T1 --> T2[".map_batches(add_interaction_features)\nDerive new columns\nRun in parallel Ray tasks"]
T2 --> T3[".filter(predicate)\n(optional)\nRow-level filtering"]
end
T3 --> SPLIT[".split_at_indices([train_size])\nProduces train_ds, val_ds"]
subgraph Training
SPLIT --> SH0["Shard 0 → Worker 0\niter_torch_batches()"]
SPLIT --> SH1["Shard 1 → Worker 1\niter_torch_batches()"]
SH0 --> EP0["Training epoch\nfor batch in shard:"]
SH1 --> EP1["Training epoch\nfor batch in shard:"]
end
subgraph Inspection
SPLIT -->|.take_batch(5)| SAMPLE["Sample batch\nfor logging / EDA"]
end
Execution model
Each .map_batches() call is not executed immediately. Ray Data builds
an execution plan and runs it lazily when data is consumed. This allows the
runtime to fuse adjacent transforms, reducing memory pressure.
ASHA (Asynchronous Successive Halving Algorithm) works in rungs:
All trials run for grace_period epochs.
The bottom 1 - 1/reduction_factor fraction are stopped.
Survivors run to the next rung (grace_period × reduction_factor epochs).
Repeat until max_t epochs.
This gives near-optimal exploration-exploitation trade-off while running
fully asynchronously – fast trials do not wait for slow ones.
5. Ray Serve Request Flow
sequenceDiagram
participant C as HTTP Client
participant Proxy as Ray Serve Proxy
participant IP as InferencePipeline<br/>(ingress replica)
participant PP as Preprocessor<br/>(replica 0 or 1)
participant BC as BatchClassifier<br/>(replica 0 or 1)
participant BQ as @serve.batch queue
C->>Proxy: POST /predict {features: [...]}
Proxy->>IP: route to InferencePipeline
IP->>PP: preprocessor.preprocess.remote(raw_features)
Note over PP: Z-score normalisation
PP-->>IP: normalised_features
IP->>BC: classifier.predict.remote(normalised_features)
BC->>BQ: enqueue single sample
Note over BQ: Wait up to batch_wait_timeout_s<br/>or until max_batch_size reached
BQ->>BC: _batched_predict([sample_0, ..., sample_k])
Note over BC: torch.no_grad()<br/>model(tensor_batch)
BC-->>IP: {probability, predicted_class, confidence}
IP-->>Proxy: {request_id, prediction, elapsed_ms}
Proxy-->>C: HTTP 200 JSON response
Throughput benefit: a single GPU forward pass over 32 samples is
significantly faster than 32 individual forward passes due to parallelism
in matrix multiplication. @serve.batch delivers this automatically
without changing the single-sample API contract seen by callers.
6. Object Store Fan-Out Pattern
flowchart TD
DRIVER["Driver"]
DATA["large_array\n= np.random.normal(size=100_000)"]
PUT["ref = ray.put(large_array)\n─ Serialise ONCE\n─ Store in Plasma\n─ Return ObjectRef"]
DRIVER --> DATA --> PUT
PUT -->|"Ref passed"| T0["Task 0\nray.get(ref) – mmap"]
PUT -->|"Ref passed"| T1["Task 1\nray.get(ref) – mmap"]
PUT -->|"Ref passed"| T2["Task 2\nray.get(ref) – mmap"]
PUT -->|"Ref passed"| T3["Task 3\nray.get(ref) – mmap"]
subgraph PlasmaStore["Plasma (Shared Memory)"]
BUF["Single buffer\n400 KB\nref-counted"]
end
PUT --> BUF
T0 -. zero-copy read .-> BUF
T1 -. zero-copy read .-> BUF
T2 -. zero-copy read .-> BUF
T3 -. zero-copy read .-> BUF
ANTI["Anti-Pattern:\nTask.remote(large_array)\n× 4 times\n→ 4 × serialise + store"]
ANTI -.->|"Avoid this"| T0