jsmpi 0.1.0

A browser-oriented MPI compatibility layer for Rust/WASM using Web Workers
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
# `jsmpi` 开发指导(第一阶段)

## 1. 文档目标

本文档用于指导 `jsmpi` 的第一阶段开发,确保实现过程围绕以下核心原则展开:

- **尽量兼容 `rsmpi` API**
- **入口改动最小化**
- **先打通浏览器多 worker 运行闭环,再扩展功能**
- **先验证真实行为,再扩展抽象与优化**

---

## 2. 第一阶段工作范围

第一阶段只做“最小可运行闭环”,不要一开始追求完整 MPI 功能。

### 必做范围

- 创建 `jsmpi` crate 基础结构
- 建立 `wasm-bindgen` 浏览器入口
- 建立 `Coordinator + Worker + WASM` 启动链路
- 提供:
  - `initialize()`
  - `Universe::world()`
  - `world.rank()`
  - `world.size()`
  - `process_at_rank(rank).send()`
  - `any_process().receive()`
  - `barrier()`
- 提供一个可运行示例:`ping_pong` / `hello_ranks`

### 暂缓范围

- 复杂 datatype 系统
- 非阻塞通信请求系统
- communicator split / cartesian topology
- 高级 reduce op / 自定义 operator
- 高性能优化

---

## 3. 推荐开发顺序

## Step 1:初始化项目骨架

建议先建立如下内容:

```text
jsmpi/
├─ Cargo.toml
├─ src/
│  ├─ lib.rs
│  ├─ environment.rs
│  ├─ topology.rs
│  ├─ runtime/
│  │  ├─ mod.rs
│  │  ├─ browser.rs
│  │  └─ protocol.rs
├─ js/
│  ├─ bootstrap.js
│  ├─ coordinator.js
│  └─ worker.js
└─ examples/
```

建议先让以下链路跑通:

1. 主线程打开页面
2. JS 创建 N 个 worker
3. 每个 worker 加载同一份 wasm 模块
4. worker 把 `rank` / `size` 注入到 Rust runtime
5. Rust 代码可读取 world 信息并输出日志

**验收标准:** 页面上能看到每个 rank 正常启动并输出自己的编号。

---

## Step 2:先做 runtime,再做 API 外观

开发顺序应为:

1. **先实现底层 runtime 通信协议**
2. **再封装 `rsmpi` 风格 API**

不要一开始就陷入 trait 细节。先确认以下事实成立:

- rank 间消息能送达
- Coordinator 能正确路由消息
- Worker 能等待消息并恢复执行
- 多个 rank 能在 barrier 汇合

**原则:真实行为优先于表面接口。**

---

## Step 3:实现最小兼容 API

第一批 API 应只覆盖用户最常写的路径:

```rust
use mpi::traits::*;

let universe = mpi::initialize().unwrap();
let world = universe.world();
let rank = world.rank();
let size = world.size();
world.barrier();
```

### 建议做法

- 模块名、类型名、方法名尽量贴近 `rsmpi`
- 暂未支持的接口先不暴露,或明确返回可理解错误
- 不要为了“名字兼容”而造出错误语义

---

## 4. 入口改造建议

用户现有 `rsmpi` 程序通常是:

```rust
fn main() {
    run();
}
```

迁移到 `jsmpi` 时,建议调整为:

```rust
use wasm_bindgen::prelude::*;

#[wasm_bindgen(start)]
pub fn start() {
    run();
}
```

或者:

```rust
#[wasm_bindgen]
pub fn jsmpi_main() {
    run();
}
```

然后把原来的核心逻辑保留在:

```rust
fn run() {
    let universe = mpi::initialize().unwrap();
    let world = universe.world();
    // 原有 MPI 逻辑基本保持不变
}
```

> 推荐规则:**把入口层改造限制在 10 行以内**,其余计算与通信逻辑保持原样。

---

## 5. 运行时实现建议

## 5.1 首版使用 Coordinator 中转

虽然 worker-to-worker 直连更快,但首版应由主线程统一中转。

优点:

- 调试简单
- 消息可观测
- barrier / broadcast / gather 更容易实现
- 容易保证正确性

### 推荐消息流

```text
Rank A worker
   -> postMessage(send)
Coordinator
   -> 查找目标 rank
   -> 投递给 Rank B worker
Rank B worker
   -> 匹配 receive 请求
```

---

## 5.2 阻塞语义实现建议

MPI 用户习惯的是阻塞式接口,但浏览器环境本质上是事件驱动的。

因此建议:

- 对外保留阻塞式/同步式 API 外观
- 内部使用消息队列 + 状态机 + Promise/回调桥接
- 在 Rust 层谨慎封装同步外观,避免卡死 UI 主线程

如果首版难以实现完全同步语义,可采用:

- worker 内部允许 async runtime 辅助
- 文档中说明浏览器环境下的语义差异

当前仓库里的建议做法是:

- 原生目标继续使用同步 `receive()` / `barrier()`
- `wasm32` 目标优先复用 `Runtime::receive_async()``Runtime::receive_with_timeout_async()`
- `wasm32` 目标在需要同步点时优先复用 `Runtime::barrier_async()`
- 示例层只保留通信意图,不重复实现轮询与超时控制

---

## 6. 代码风格约束

### 6.1 基本原则

- 保持模块职责单一
- 运行时协议与公共 API 分离
- 所有公共类型必须写文档注释
- 错误类型统一映射到 `jsmpi::Error`

### 6.2 命名建议

- 面向用户的命名:尽量对齐 `rsmpi`
- 内部 runtime 命名:清晰表达浏览器语义,如 `CoordinatorBridge``PendingReceive`

### 6.3 不建议做的事

- 不要把 JS 互操作逻辑散落在所有模块中
- 不要过早抽象复杂 trait 层级
- 不要在未验证实际通信前写大段兼容包装代码

---

## 7. 测试与验证策略

开发时必须遵循以下验证顺序:

## 7.1 先做最小真实示例

每实现一个阶段,都要优先验证真实浏览器行为。

推荐测试顺序:

1. 单 rank 启动
2. 双 rank 启动
3. rank0 -> rank1 单次发送
4. rank1 正确接收
5. 所有 rank barrier 汇合

## 7.2 示例优先于 mock

不要用大量 mock 证明消息能通信,应优先运行真实浏览器示例。

推荐示例:

- `examples/hello_ranks.rs`
- `examples/ping_pong.rs`
- `examples/broadcast_sum.rs`

---

## 8. P0 完成验收指令(当前基线)

在进入 P1 前,至少完成以下本地验收:

```bash
cargo check --lib
cargo test --lib
cargo check --examples --target wasm32-unknown-unknown
cd demo && trunk build
cd .. && npm run test:browser:e2e
```

说明:

- `npm run test:browser:e2e` 当前会执行 3 轮完整浏览器示例套件。
-`wasm-bindgen` schema 报错,请对齐 `wasm-bindgen-cli` 版本到 `0.2.117`
**判断标准:** 示例真实运行成功,优先级高于单纯单元测试通过。

---

## 8.1 P1 超时与重试策略基线

当前已在 `Runtime` 层提供可配置重试策略(`RetryPolicy`),覆盖:

- `send_with_retry_timeout(...)`
- `receive_with_retry_timeout(...)`
- `barrier_with_retry_timeout(...)`

策略参数:

- `max_retries`
- `initial_backoff`
- `max_backoff`
- `backoff_multiplier`

退化行为验证(当前最小基线):

```bash
cargo test --lib runtime::state::tests::receive_with_retry_timeout_stops_after_budget
cargo test --lib runtime::state::tests::retry_policy_backoff_is_capped
```

验证目标:

- 在持续超时场景下,重试次数受预算上限约束。
- 退避时间按策略增长但受 `max_backoff` 限制,不出现无界增长。

---

## 8. 第一阶段的依赖建议

`Cargo.toml` 初始可考虑:

```toml
[dependencies]
wasm-bindgen = "0.2"
js-sys = "0.3"
serde = { version = "1", features = ["derive"] }
serde-wasm-bindgen = "0.6"
console_error_panic_hook = "0.1"
```

如需浏览器 API:

```toml
web-sys = { version = "0.3", features = [
  "Worker",
  "MessageEvent",
  "DedicatedWorkerGlobalScope",
  "console",
] }
```

> `rsmpi` 本身依赖原生 MPI 环境,`jsmpi` 不应直接照搬其底层实现,而应主要复刻其用户层 API 体验。

---

## 9. 示例迁移策略

为了兑现“几乎不改业务代码”的目标,建议建立一个迁移模板。

### 原始 `rsmpi` 示例

```rust
fn main() {
    let universe = mpi::initialize().unwrap();
    let world = universe.world();
    // ...
}
```

### 迁移后 `jsmpi` 示例

```rust
use wasm_bindgen::prelude::*;
use mpi::traits::*;

#[wasm_bindgen(start)]
pub fn start() {
    let universe = mpi::initialize().unwrap();
    let world = universe.world();
    // ... 原逻辑尽量不变
}
```

迁移检查表:

- [ ] `use mpi::traits::*` 不变
- [ ] `initialize/world/rank/size` 不变
- [ ] `send/receive/barrier` 调用不变
- [ ] 只修改入口层

---

## 10. 第一阶段交付定义

当满足以下条件时,可认为第一阶段完成:

1. `jsmpi` crate 能成功编译为 `wasm`2. 浏览器可创建多个 worker 并运行同一 Rust 程序。
3. 应用代码可通过 `initialize().world()` 获取 rank/size。
4. 至少支持一次真实的 `send/receive`5. 至少支持一次真实的 `barrier`6. 有一个文档化 demo 展示“从 rsmpi 风格代码迁移到浏览器”的最小改动方案。

---

## 11. 下一步实际任务清单

建议马上进入如下执行项:

1. 创建 `Cargo.toml``src/lib.rs`
2. 先实现浏览器启动最小骨架
3. 增加 rank/size 注入与读取
4. 打通 `send/receive` 消息协议
5. 编写 `hello_ranks``ping_pong` 示例
6. 再回头补齐更广的 `rsmpi` API 包装

> 开发节奏建议:**每完成一个 API,就用真实多 worker 示例验证一次**,避免后期集中排错。