Skip to content

Commit 2c751d2

Browse files
lterracclaude
andcommitted
docs(platform): add architecture, Simpler integration, and model-support interface docs
- architecture.md: instance roles, module lifecycle, channel model, deferred scope - simpler-integration.md: replica device assignment, RuntimeBinaries, processFc contract, hot-path staging and future in-device tensor channels - model-support-interface.md: what Model Support provides vs owns, RuntimePlan future contract - README.md: updated index linking all new and existing docs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent a26a4d1 commit 2c751d2

4 files changed

Lines changed: 226 additions & 5 deletions

File tree

platform/docs/README.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,27 @@ platform/
2222
build/ generated build output; not documented here
2323
```
2424

25-
## Module Areas
25+
## Documentation
2626

27+
- [Architecture](architecture.md): instance roles, module system, channel model, and scope.
28+
- [Simpler Integration](simpler-integration.md): how replica ranks launch Simpler, the `processFc` interface, and the path to in-device tensor channels.
29+
- [Model Support Interface](model-support-interface.md): what Model Support owns, provides, and will consume from the platform.
2730
- [System](system/README.md): engine lifecycle and cross-instance start/stop control.
2831
- [Channels](system/channels.md): payload, coordination, metadata, input, output, and message primitives.
29-
30-
Configuration types, the service module, channel controller, and broadcast deployment are tracked in issue #32 and are not part of this initial PR.
32+
- [Modules](modules/README.md): configuration, service, channel controller, and broadcast deployment.
3133

3234
## Runtime Shape
3335

3436
The current platform runtime is built around `serving::system::Engine`. The engine owns a set of `serving::modules::Module` instances, initializes them, starts them across instances through RPC, waits for termination, and finalizes them.
3537

36-
This initial PR covers the following building blocks:
38+
This PR covers the following building blocks:
3739

3840
- Engine lifecycle: cross-instance start/stop over RPC (`serving::system::Engine`).
3941
- Module base interface: initialize/run/terminate/await/finalize lifecycle with optional `taskr::Service` (`serving::modules::Module`).
4042
- Channel primitives: `Input`, `Output`, `Message`, and `MessageTypeRegistry` for host-side control traffic.
43+
- Configuration types: `Deployment`, `Partition`, `Task`, `Edge`, `Replica`, `RequestManager` with JSON serialization and verification.
44+
- `broadcastDeployment`: deployer-to-worker deployment config distribution over RPC.
45+
- `channelController`: desired-vs-actual reconciliation loop for host-side SPSC channels.
46+
- `service`: wraps `taskr::Runtime` for cooperative background services.
4147

42-
Deployment graph representation, deployment broadcast, desired-state channel creation, dynamic scaling, topology-aware replacement, fault recovery, and Python bindings are not implemented in this PR.
48+
Dynamic scaling, fault recovery, `channelDispatcher`, `taskScheduler`, executor roles, heartbeat, `RuntimePlan` update protocol, in-device tensor channels, and Python bindings are deferred to follow-up PRs.

platform/docs/architecture.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Platform Architecture
2+
3+
## What the platform layer is
4+
5+
The platform layer is the **host-side control plane** for a distributed PyPTO Serving deployment. It manages:
6+
7+
- Instance lifecycle (start, stop, health)
8+
- Deployment configuration distribution
9+
- Host-side channel creation and teardown
10+
- Module initialization and service scheduling
11+
12+
It is not a model execution layer. Model kernels, tensor movement, KV cache, batching, and token scheduling belong to Model Support and are executed through Simpler on each device. The platform exists so that Model Support does not have to manage MPI ranks, HiCR channels, or distributed lifecycle directly.
13+
14+
## Instance roles
15+
16+
Every MPI rank in a deployment plays one of four roles, determined by its instance ID and the deployment configuration:
17+
18+
| Role | Count | Responsibility |
19+
|---|---|---|
20+
| **Deployer** | 1 (root) | Reads deployment config, broadcasts it to all other ranks, then transitions to a coordinator or replica role |
21+
| **Coordinator** | one per partition | Routes jobs to replicas, accumulates outputs, fires completion callbacks |
22+
| **Replica** | one or more per partition | Executes model computation via Simpler on a dedicated NPU device |
23+
| **Request Manager** | 1 | Entry point for client requests; maps to the partition owning the user-interface edge |
24+
25+
Device assignment for replicas: `deviceId = instanceId - numPartitions`. Each replica rank owns exactly one NPU.
26+
27+
## Module system
28+
29+
All platform behaviour is expressed as `serving::modules::Module` instances owned by the `serving::system::Engine`. The engine drives a fixed lifecycle:
30+
31+
```
32+
initialize() → run() → [service loop] → terminate() → await() → finalize()
33+
```
34+
35+
Modules can register a periodic background service (via `taskr::Service`) that runs inside the service loop between the `run()` and `terminate()` phases. The engine coordinates the lifecycle of all instances over RPC, so every rank progresses through the same phases in lockstep.
36+
37+
### Modules in this PR
38+
39+
| Module | Purpose |
40+
|---|---|
41+
| `broadcastDeployment` | Deployer sends deployment JSON to all workers over RPC at initialize time |
42+
| `channelController` | Desired-vs-actual reconciliation loop; creates and tears down HiCR SPSC channels |
43+
| `service` | Wraps `taskr::Runtime`; owns and drives background `taskr::Service` instances |
44+
45+
### Deferred modules (next PRs)
46+
47+
| Module | Purpose |
48+
|---|---|
49+
| `channelDispatcher` | Polls subscribed input channels; dispatches messages to registered handlers |
50+
| `taskScheduler` | Registers named `taskr::Task` instances; drives taskr through the module lifecycle |
51+
| `roles::coordinator` | Job queue and replica dispatch; completion callback when all outputs are gathered |
52+
| `roles::replica` | Receives coordinator input, invokes `processFc`, returns outputs |
53+
| `heartbeat` | Periodic health check between coordinators and replicas |
54+
55+
## Channel model
56+
57+
All channels in this PR are **host-side HiCR SPSC channels** carrying variable-size payloads and fixed-size metadata. They are used for control traffic and small tensors.
58+
59+
In-device tensor channels — where the hot path (prefill/decode token data) moves directly between NPU devices without staging through host memory — are **not implemented here**. They are the next major milestone and unblock the TP/PP tensor data path.
60+
61+
## What is deliberately out of scope
62+
63+
- Per-token scheduling (hot path)
64+
- KV cache management
65+
- Batching and sampling policy
66+
- Python bindings
67+
- `RuntimePlan` update protocol and watch/subscribe API
68+
- Dynamic scaling, drain, and fault recovery
69+
- Topology-aware placement
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Model Support Interface
2+
3+
This document describes what the Model Support layer needs to know and control when integrating with the platform.
4+
5+
## What the platform tells you
6+
7+
At startup, after `broadcastDeployment` completes, every rank knows:
8+
9+
- **Its role**: coordinator, replica, or request manager — determined by instance ID and the deployment config
10+
- **Its device index** (replicas only): `deviceId = instanceId - numPartitions`
11+
- **The full deployment graph**: partitions, tasks, edges, and which edges carry user-interface traffic
12+
13+
Model Support should use this information to initialize Simpler on replica ranks before the engine starts.
14+
15+
## What you provide
16+
17+
### 1. Compiled Simpler artifacts
18+
19+
Replica ranks need the five runtime library paths in `RuntimeBinaries`. The platform does not locate or validate them. Pass them via command-line arguments or configuration alongside the deployment JSON.
20+
21+
### 2. A `processFc` per task function name
22+
23+
For each task declared in the deployment configuration, Model Support registers a function:
24+
25+
```cpp
26+
std::function<void(serving::modules::roles::TaskContext &context)>
27+
```
28+
29+
This is the only point where Model Support code runs during execution. Everything else — job routing, input aggregation, output forwarding, channel lifecycle — is handled by the platform.
30+
31+
### 3. Request/response edge names
32+
33+
The deployment config declares a `RequestManager` with an input edge name and an output edge name. Model Support must ensure:
34+
35+
- The input edge name matches the edge from which user requests arrive
36+
- The output edge name matches the edge to which results are written
37+
- The task that reads the input edge does not have any other inter-partition inputs
38+
- The task that writes the output edge does not have any other inter-partition outputs
39+
40+
These constraints are verified by `Deployment::verify()` at startup.
41+
42+
## What you do not own
43+
44+
| Concern | Owner |
45+
|---|---|
46+
| MPI rank management | Platform (via HiCR `InstanceManager`) |
47+
| Channel creation and teardown | Platform (`channelController` module) |
48+
| Deployment broadcast | Platform (`broadcastDeployment` module) |
49+
| Job routing from coordinator to replica | Platform (`coordinator::Module`) |
50+
| Input aggregation and output forwarding | Platform (`replica::Module`) |
51+
| Engine lifecycle (init/run/terminate) | Platform (`Engine`) |
52+
| Replica health monitoring | Platform (heartbeat, future) |
53+
54+
## RuntimePlan — future interface
55+
56+
The current PR does not yet expose a `RuntimePlan`. A future milestone will provide a versioned, observable snapshot of what the platform has actually instantiated:
57+
58+
- Which replicas are live, draining, or unavailable
59+
- Which channels exist and what their endpoints are
60+
- Safe `active → draining → removed` state transitions before any channel is deleted
61+
62+
Model Support will consume this object to make routing and scheduling decisions (KV-locality-aware replica selection, drain-aware request assignment, channel handle lookup) without duplicating platform resource state. The same object will be queryable by operators and higher-level control loops for observability.
63+
64+
Until `RuntimePlan` is implemented, Model Support should treat the deployment configuration as the ground truth of what is running.
65+
66+
## Tensor channel hot path — future
67+
68+
All channels are currently host-side. For the prefill/decode token data path, in-device tensor channels will carry tensor payloads directly between NPU devices without staging through host memory. When implemented:
69+
70+
- Replica `processFc` will receive device-memory `LocalMemorySlot` handles rather than host buffers
71+
- `toDevice` / `toHost` staging in the process function becomes unnecessary
72+
- The `processFc` signature does not change
73+
74+
Model Support should design the process function to be forward-compatible: check whether the incoming `LocalMemorySlot` is a host or device buffer and branch accordingly.
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Simpler Integration
2+
3+
## Which ranks launch Simpler
4+
5+
Only **replica** ranks initialize a Simpler runtime instance. Coordinator and request manager ranks are pure host-side and never touch device memory or Simpler APIs.
6+
7+
Device assignment: `deviceId = instanceId - numPartitions`
8+
9+
With two partitions and one replica each, ranks 0 and 1 are coordinators, ranks 2 and 3 are replicas owning devices 0 and 1 respectively.
10+
11+
## Initialization
12+
13+
Each replica rank initializes Simpler in `main()` before the serving engine starts:
14+
15+
```cpp
16+
hllm::simpler::RuntimeBinaries bins{hostLib, aicpuLib, aicoreKernel, dispatcherLib, simplerLogLib};
17+
auto rt = std::make_unique<hllm::simpler::SimplerRuntime>();
18+
rt->init(bins, deviceId);
19+
mnist::loadKernels(*rt, artifactDir);
20+
```
21+
22+
`RuntimeBinaries` holds five paths to compiled runtime artifacts:
23+
24+
| Field | File | Purpose |
25+
|---|---|---|
26+
| `host` | `libhost_runtime.so` | Core device runtime |
27+
| `aicpu` | `libaicpu_kernel.so` | CPU-side kernel support |
28+
| `aicore` | `aicore_kernel.o` | NPU core kernel binary |
29+
| `dispatcher` | `libsimpler_aicpu_dispatcher.so` | On-device dispatcher |
30+
| `simplerLog` | `libsimpler_log.so` | Logging (preloaded RTLD_GLOBAL) |
31+
32+
Model Support is responsible for providing and locating these artifacts. The platform does not interpret or validate them.
33+
34+
## `processFc` — the task execution callback
35+
36+
The replica module accepts a user-provided function with signature:
37+
38+
```cpp
39+
std::function<void(serving::modules::roles::TaskContext &context)>
40+
```
41+
42+
The platform calls this function once per job, after all input dependencies have arrived. Inside the function, Model Support:
43+
44+
1. Reads inputs from `context.getInput(edgeName)` — returns a `LocalMemorySlot` containing the host buffer
45+
2. Stages inputs to device with `rt->toDevice(ptr, bytes)`
46+
3. Dispatches a Simpler kernel with `rt->run(callableId, args, config)`
47+
4. Stages outputs back with `rt->toHost(hostPtr, devPtr, bytes)`
48+
5. Registers outputs with `context.setOutput(edgeName, ptr, size)`
49+
50+
The platform guarantees that:
51+
- All declared inputs are present and ready before the call
52+
- Outputs registered via `setOutput` are forwarded to the coordinator after the call returns
53+
- The function is called at most once per job; re-entrancy is not required
54+
55+
## `SimplerRuntime` API surface
56+
57+
```cpp
58+
void init(const RuntimeBinaries &bins, int deviceId);
59+
void loadCallable(const void *blob, size_t size, uint32_t id);
60+
void run(uint32_t callableId, ChipStorageTaskArgs &args, ChipCallConfig &config);
61+
void *toDevice(const void *hostPtr, size_t bytes);
62+
void toHost(void *hostPtr, const void *devPtr, size_t bytes);
63+
void *alloc(size_t bytes);
64+
void free(void *devPtr);
65+
void finalize();
66+
```
67+
68+
## Channel model and hot path
69+
70+
All channels in the current platform are host-side. Tensor payloads passed through `processFc` are staged through host memory (`toDevice` / `toHost`). This is correct for control traffic and small tensors but adds latency on the hot path for large prefill/decode tensors.
71+
72+
**In-device tensor channels** (direct NPU-to-NPU transfer without host staging) are the next milestone. When implemented, the `processFc` interface will remain the same — Model Support will simply receive device-memory `LocalMemorySlot` handles instead of host buffers, and the staging calls become unnecessary.

0 commit comments

Comments
 (0)