# `jsmpi` 技术规范(草案 v0.1)
## 1. 目标
`jsmpi` 是一个面向浏览器运行时的 Rust crate,目标是在 **Rust MPI 程序编译为 WebAssembly 后,通过 Web Worker 在浏览器中模拟/实现 MPI 执行模型**。
核心要求:
- **API 与 `rsmpi` 尽可能保持一致**。
- 对现有 `rsmpi` Rust 程序,除**程序入口与初始化方式**外,业务逻辑代码应当**几乎无需改动**。
- 支持在现代浏览器中运行多 worker 并完成消息传递、同步与常用 collective。
- 优先保证**单页应用内、本地浏览器环境**下的可用性与可调试性。
---
## 2. 非目标(当前阶段)
以下内容不作为 v0.1 首要目标:
- 与原生 MPI 二进制网络互通。
- 多浏览器、多设备、跨网络集群部署。
- 完整覆盖所有 MPI 扩展接口、拓扑通信器、RMA/One-sided、MPI-IO。
- 强实时性能对标原生 OpenMPI / MPICH。
---
## 3. 兼容性原则
## 3.1 API 对齐目标
`jsmpi` 应尽量复刻 `rsmpi` 的以下使用模式:
```rust
use mpi::traits::*;
fn main() {
let universe = mpi::initialize().unwrap();
let world = universe.world();
let rank = world.rank();
let size = world.size();
if rank == 0 {
world.process_at_rank(1).send(&42_i32);
} else if rank == 1 {
let (msg, _status) = world.any_process().receive::<i32>();
println!("{msg}");
}
}
```
兼容目标分级:
| L1 | 现有 `use mpi::traits::*` 风格基本不变 |
| L2 | `initialize` / `world` / `rank` / `size` / `send` / `receive` / `broadcast` / `barrier` 等常用接口保持一致 |
| L3 | 泛型、traits、返回值结构尽量贴近 `rsmpi` |
| L4 | 仅在入口层增加 `jsmpi` 浏览器启动胶水代码 |
## 3.2 允许改动点
仅允许下列最小改动:
1. 程序入口从原生 `fn main()` 调整为浏览器/WASM 入口包装。
2. 构建目标从本地 target 改为 `wasm32-unknown-unknown`。
3. 在 HTML/JS 启动层指定 worker 数量、模块 URL、启动参数。
业务计算逻辑、通信调用、collective 调用方式应尽量保持不变。
---
## 4. 总体架构
## 4.1 运行模型
浏览器中的一次 MPI 作业由以下组件组成:
- **主线程(Coordinator)**
- 负责创建 Web Workers。
- 分配 rank / size / job_id。
- 维护 worker 生命周期。
- 作为默认消息路由与 barrier/collective 协调者。
- **Worker(Rank Runtime)**
- 每个 Web Worker 对应一个 MPI rank。
- 加载同一份 WASM 模块。
- 调用 Rust 导出的 rank 执行入口。
- **WASM 模块(Rust + `jsmpi`)**
- 提供与 `rsmpi` 对齐的 API 层。
- 将 MPI 调用映射为对 JS runtime bridge 的请求。
## 4.2 逻辑分层
```text
应用代码(rsmpi 风格)
│
▼
jsmpi 兼容 API 层
│
▼
浏览器运行时抽象层(Runtime Adapter)
│
├─ Worker 内消息收发
├─ 主线程调度/路由
└─ 序列化/同步机制
```
---
## 5. 模块划分
建议 crate 初始目录结构如下:
```text
jsmpi/
├─ src/
│ ├─ lib.rs
│ ├─ environment.rs # initialize / Universe
│ ├─ topology.rs # SystemCommunicator / Process
│ ├─ point_to_point.rs # send / receive / probe
│ ├─ collective.rs # barrier / broadcast / reduce(逐步)
│ ├─ request.rs # 非阻塞接口预留
│ ├─ datatype.rs # Rust <-> message payload 映射
│ ├─ runtime/
│ │ ├─ mod.rs
│ │ ├─ browser.rs # wasm 环境桥接
│ │ ├─ protocol.rs # worker/coordinator 协议
│ │ └─ state.rs # rank 本地状态机
│ └─ error.rs
├─ js/
│ ├─ coordinator.js
│ ├─ worker.js
│ └─ bootstrap.js
├─ examples/
└─ docs/
```
---
## 6. 初始化与生命周期规范
## 6.1 初始化接口
目标接口:
- `mpi::initialize()`
- `Universe::world()`
- `Communicator::rank()`
- `Communicator::size()`
浏览器语义下:
- `initialize()` 从 worker 注入环境或全局 JS bridge 中读取:
- `rank`
- `size`
- `job_id`
- runtime 通信端口
- `Universe` 为轻量上下文句柄,不负责真正的系统级 MPI runtime 关闭。
## 6.2 退出语义
- Worker 执行结束后,应向 Coordinator 发送 `rank_finished` 事件。
- 所有 rank 完成后,Coordinator 汇总作业状态。
- `abort` 在 v0.1 可以退化为:记录错误并结束整个 job。
P0 已实现的生命周期状态语义:
- `running`:作业已启动并开始接收 rank 事件。
- `finished`:所有 rank 发出 `finished` 事件。
- `stopped`:出现 worker crash 或协议级错误(如 envelope 校验失败)。
- `timeout`:`send/receive/barrier` 的超时路径返回 `Error::Timeout`。
P1 已补齐的可靠性语义:
- 提供 `RetryPolicy`(重试次数、初始退避、最大退避、退避倍数)。
- `send/receive/barrier` 提供带重试的超时接口。
- 在持续超时场景下,重试遵循预算上限并保证退避时间有上界。
---
## 7. 通信协议规范
## 7.1 消息基本结构
建议统一使用结构化消息:
```ts
interface JsmpiEnvelope {
protocolVersion: number;
kind: "point_to_point";
jobId: string;
src: number;
dst: number;
tag: number;
seq: number;
}
```
当前实现基线(P0):
- `protocol_version = 1`
- 版本不匹配返回 `PROTO_UNSUPPORTED_VERSION`
- 非法 rank / 非法 envelope 返回 `PROTO_INVALID_ENVELOPE`
- worker 与 launcher 双侧都执行 envelope 基础校验
## 7.2 点对点通信
首阶段支持:
- `send`
- `receive`
- `receive_into`
- `any_process().receive`
- `process_at_rank(n)`
设计原则:
- 默认由 Coordinator 进行路由,简化 worker 间拓扑控制。
- 先实现**可靠、有序、阻塞式**语义。
- 使用本地队列缓存尚未匹配的消息。
- `tag` 与 `source` 匹配规则尽量贴近 MPI。
## 7.3 Collective 通信
v0.1 优先级:
1. `barrier`
2. `broadcast`
3. `gather`
4. `scatter`
5. `reduce` / `all_reduce`
实现建议:
- 首版全部由 Coordinator 聚合协调。
- 后续版本可优化为树形广播/归约。
---
## 8. 数据类型与序列化
## 8.1 v0.1 支持范围
优先支持:
- 标量:`i32`, `i64`, `u32`, `u64`, `f32`, `f64`, `bool`
- 连续切片:`&[T]`, `&mut [T]`
- 常见 `Vec<T>` 接收场景
## 8.2 编码方式
推荐两层策略:
1. **TypedArray 直传**:用于数值类型切片,提高效率。
2. **Serde/二进制编码**:用于复杂结构,作为扩展层。
首版可先聚焦 POD/基础数值类型,避免过早引入复杂 datatype 系统。
---
## 9. Rust API 设计约束
## 9.1 命名兼容
公共 API 命名、traits、模块路径应尽量接近 `rsmpi`。
建议:
- 暴露 `traits` 模块
- 暴露 `topology`, `environment`, `datatype` 等模块
- 为常见调用链提供同名方法
## 9.2 trait 兼容策略
- 首先兼容用户最常用的 trait 导入路径。
- 对暂未支持的 API,使用清晰的 `unimplemented` / feature gate / 文档标注。
- 避免为了兼容而暴露明显错误的假语义。
---
## 10. 浏览器运行时要求
- 目标平台:`wasm32-unknown-unknown`
- 建议配套:`wasm-bindgen`, `js-sys`, `web-sys`
- 需要支持:
- `Worker`
- `MessageChannel` / `postMessage`
- `structured clone`
- `Promise` / 事件循环
可选增强:
- `SharedArrayBuffer` + `Atomics`(后续优化)
- OffscreenCanvas(若示例涉及可视化)
---
## 11. 错误处理与调试
- 所有 runtime 协议错误应映射到统一的 `jsmpi::Error`。
- 在 debug 模式下输出:
- rank 启动日志
- 消息发送/接收日志
- barrier 进入/退出日志
- 推荐增加 `console_error_panic_hook` 便于浏览器内排障。
---
## 12. 性能策略
初期优先级:**正确性 > 兼容性 > 可调试性 > 性能**。
后续优化方向:
- 零拷贝/转移 `ArrayBuffer`
- 批量消息投递
- collective 树形算法
- `SharedArrayBuffer` 降低 coordinator 瓶颈
---
## 13. 里程碑计划
## M1:最小可运行版本
交付内容:
- `initialize`, `world`, `rank`, `size`
- `process_at_rank().send`
- `any_process().receive`
- `barrier`
- 浏览器示例:2~4 rank hello world / ping-pong
## M2:常用 collectives
交付内容:
- `broadcast`
- `gather` / `scatter`
- `reduce` / `all_reduce`(基础数值类型)
## M3:兼容性增强
交付内容:
- 更多 `rsmpi` trait 对齐
- 示例迁移:将已有 `rsmpi` demo 以最小改动迁移到浏览器
- 初步文档与对比矩阵
---
## 14. 测试规范
测试必须分层:
1. **单元测试**:协议匹配、队列、tag/source 过滤。
2. **wasm 浏览器集成测试**:多 worker 真正收发。
3. **兼容性样例测试**:直接运行 `rsmpi` 风格示例,验证改动最小。
关键验证项:
- 消息顺序正确
- barrier 不死锁
- broadcast 数据一致
- rank/size 赋值正确
- 异常作业能被主线程感知
---
## 15. 风险与待确认项
- 浏览器 event loop 与阻塞式 MPI 语义存在天然张力,需要通过异步桥接 + 同步外观设计。
- `rsmpi` 完整 datatype/trait 体系较复杂,应分阶段兼容。
- 若需要更高性能,后续可能引入 `SharedArrayBuffer`,同时要求 COOP/COEP 头配置。
---
## 16. 下一步实施建议
下一阶段应按以下顺序推进:
1. 初始化 crate 基础结构与 feature flags。
2. 打通 `coordinator.js` + `worker.js` + Rust WASM 入口。
3. 实现 `initialize/world/rank/size`。
4. 实现阻塞式 `send/receive` 最小闭环。
5. 基于真实浏览器示例验证 2-rank ping-pong。
> 结论:`jsmpi` 应先以“**浏览器内 MPI 兼容层**”定位落地,优先建立 `rsmpi` 风格 API 与 Web Worker runtime 之间的稳定桥接。