295 lines
6.9 KiB
Markdown
295 lines
6.9 KiB
Markdown
|
|
# Phase 11.5c: Coroutine実装による非同期処理の完成
|
|||
|
|
|
|||
|
|
## 🎯 目標
|
|||
|
|
async/await構文を追加し、JITレベルでcoroutineをstate machineに変換して高性能な非同期処理を実現する。
|
|||
|
|
|
|||
|
|
## 📊 現状と目標
|
|||
|
|
|
|||
|
|
### 現在の非同期処理
|
|||
|
|
```nyash
|
|||
|
|
// nowait文(スレッドベース)
|
|||
|
|
nowait result = heavyComputation()
|
|||
|
|
// ...
|
|||
|
|
local value = wait result // FutureBoxから取得
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 目標の構文
|
|||
|
|
```nyash
|
|||
|
|
// async/await(coroutineベース)
|
|||
|
|
async function fetchData(url) {
|
|||
|
|
local response = await httpGet(url)
|
|||
|
|
local json = await response.json()
|
|||
|
|
return json.data
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 複数の非同期処理
|
|||
|
|
async function fetchAll(urls) {
|
|||
|
|
local promises = []
|
|||
|
|
loop(url in urls) {
|
|||
|
|
promises.push(fetchData(url))
|
|||
|
|
}
|
|||
|
|
return await Promise.all(promises)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🚀 実装計画
|
|||
|
|
|
|||
|
|
### Step 1: AST拡張
|
|||
|
|
```rust
|
|||
|
|
// ast.rs
|
|||
|
|
#[derive(Debug, Clone)]
|
|||
|
|
pub enum ASTNode {
|
|||
|
|
// 既存
|
|||
|
|
Function { name: String, params: Vec<String>, body: Box<ASTNode> },
|
|||
|
|
|
|||
|
|
// 新規追加
|
|||
|
|
AsyncFunction {
|
|||
|
|
name: String,
|
|||
|
|
params: Vec<String>,
|
|||
|
|
body: Box<ASTNode>
|
|||
|
|
},
|
|||
|
|
AwaitExpression {
|
|||
|
|
expression: Box<ASTNode>
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Step 2: MIR Coroutine命令
|
|||
|
|
```rust
|
|||
|
|
// mir/instruction.rs
|
|||
|
|
pub enum MirInstruction {
|
|||
|
|
// 既存命令...
|
|||
|
|
|
|||
|
|
// Coroutine関連
|
|||
|
|
/// Coroutine生成
|
|||
|
|
CoroutineCreate {
|
|||
|
|
dst: ValueId,
|
|||
|
|
func: FunctionId,
|
|||
|
|
captures: Vec<ValueId>,
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/// Yield point(await)
|
|||
|
|
CoroutineYield {
|
|||
|
|
value: ValueId,
|
|||
|
|
resume_label: BasicBlockId,
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/// Coroutine再開
|
|||
|
|
CoroutineResume {
|
|||
|
|
dst: Option<ValueId>,
|
|||
|
|
coroutine: ValueId,
|
|||
|
|
value: Option<ValueId>,
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/// Promise結合
|
|||
|
|
PromiseAll {
|
|||
|
|
dst: ValueId,
|
|||
|
|
promises: Vec<ValueId>,
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Step 3: State Machine変換
|
|||
|
|
```rust
|
|||
|
|
// mir/coroutine_transform.rs
|
|||
|
|
pub struct CoroutineTransform {
|
|||
|
|
state_enum: HashMap<FunctionId, StateEnum>,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// async関数をstate machineに変換
|
|||
|
|
impl CoroutineTransform {
|
|||
|
|
pub fn transform_async_function(&mut self,
|
|||
|
|
func: &MirFunction
|
|||
|
|
) -> MirFunction {
|
|||
|
|
// 1. await pointsを収集
|
|||
|
|
let await_points = self.collect_await_points(func);
|
|||
|
|
|
|||
|
|
// 2. State enum生成
|
|||
|
|
let states = self.generate_states(await_points);
|
|||
|
|
|
|||
|
|
// 3. State machine関数生成
|
|||
|
|
let state_machine = self.build_state_machine(func, states);
|
|||
|
|
|
|||
|
|
// 4. Coroutine wrapper生成
|
|||
|
|
self.wrap_as_coroutine(state_machine)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fn build_state_machine(&self,
|
|||
|
|
func: &MirFunction,
|
|||
|
|
states: Vec<State>
|
|||
|
|
) -> MirFunction {
|
|||
|
|
// switch(state) {
|
|||
|
|
// case State0: ... goto await1 or return
|
|||
|
|
// case State1: ... goto await2 or return
|
|||
|
|
// ...
|
|||
|
|
// }
|
|||
|
|
|
|||
|
|
let mut builder = MirBuilder::new();
|
|||
|
|
|
|||
|
|
// Coroutine state
|
|||
|
|
let state_var = builder.emit_local("state");
|
|||
|
|
let locals = builder.emit_local("locals");
|
|||
|
|
|
|||
|
|
// Main dispatch loop
|
|||
|
|
let loop_bb = builder.create_block();
|
|||
|
|
builder.emit_jump(loop_bb);
|
|||
|
|
|
|||
|
|
builder.switch_to_block(loop_bb);
|
|||
|
|
let state = builder.emit_load(state_var);
|
|||
|
|
|
|||
|
|
// State dispatch
|
|||
|
|
for (i, state) in states.iter().enumerate() {
|
|||
|
|
let state_bb = builder.create_block();
|
|||
|
|
builder.emit_case(state.id, state_bb);
|
|||
|
|
|
|||
|
|
builder.switch_to_block(state_bb);
|
|||
|
|
self.emit_state_code(&mut builder, state);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
builder.build()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Step 4: JIT Coroutine最適化
|
|||
|
|
```rust
|
|||
|
|
// jit/lower/coroutine.rs
|
|||
|
|
impl<'a> LoweringBuilder<'a> {
|
|||
|
|
/// Coroutineのstack switching最適化
|
|||
|
|
fn emit_coroutine_switch(&mut self,
|
|||
|
|
from: Value,
|
|||
|
|
to: Value
|
|||
|
|
) {
|
|||
|
|
if self.config.use_fast_switch {
|
|||
|
|
// レジスタのみ保存(最適化)
|
|||
|
|
self.emit_save_registers();
|
|||
|
|
self.emit_switch_stack(to);
|
|||
|
|
self.emit_restore_registers();
|
|||
|
|
} else {
|
|||
|
|
// フルコンテキスト保存
|
|||
|
|
self.emit_full_context_save(from);
|
|||
|
|
self.emit_full_context_restore(to);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Inline可能なawaitの検出
|
|||
|
|
fn try_inline_await(&mut self,
|
|||
|
|
await_expr: &MirInstruction
|
|||
|
|
) -> Option<Value> {
|
|||
|
|
// Promise.resolvedなら即値返却
|
|||
|
|
if self.is_resolved_promise(await_expr) {
|
|||
|
|
return Some(self.emit_immediate_value());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 小さなasync関数ならインライン化
|
|||
|
|
if self.is_small_async(await_expr) {
|
|||
|
|
return Some(self.inline_async_call(await_expr));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
None
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Step 5: ランタイムサポート
|
|||
|
|
```rust
|
|||
|
|
// boxes/promise_box.rs
|
|||
|
|
pub struct PromiseBox {
|
|||
|
|
state: Arc<Mutex<PromiseState>>,
|
|||
|
|
base: BoxBase,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
enum PromiseState {
|
|||
|
|
Pending(Vec<Box<dyn FnOnce(Box<dyn NyashBox>)>>),
|
|||
|
|
Resolved(Box<dyn NyashBox>),
|
|||
|
|
Rejected(String),
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
impl PromiseBox {
|
|||
|
|
pub fn all(promises: Vec<Box<PromiseBox>>) -> Box<PromiseBox> {
|
|||
|
|
// 全Promise完了待ち
|
|||
|
|
let result = PromiseBox::new();
|
|||
|
|
let remaining = Arc::new(AtomicUsize::new(promises.len()));
|
|||
|
|
|
|||
|
|
for (i, promise) in promises.iter().enumerate() {
|
|||
|
|
promise.then(move |value| {
|
|||
|
|
// 結果収集
|
|||
|
|
if remaining.fetch_sub(1, Ordering::SeqCst) == 1 {
|
|||
|
|
result.resolve(collected_values);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Box::new(result)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 📈 期待される効果
|
|||
|
|
|
|||
|
|
### パフォーマンス比較
|
|||
|
|
```nyash
|
|||
|
|
// Before: Thread-based
|
|||
|
|
nowait results = []
|
|||
|
|
loop(url in urls) {
|
|||
|
|
nowait r = httpGet(url) // 新スレッド生成
|
|||
|
|
results.push(r)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// After: Coroutine-based
|
|||
|
|
async function fetchAll(urls) {
|
|||
|
|
return await Promise.all(
|
|||
|
|
urls.map(url => httpGet(url)) // Stack switching only
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 改善予測
|
|||
|
|
- メモリ使用量: 1/100(スレッドstack vs coroutine state)
|
|||
|
|
- コンテキストスイッチ: 1000倍高速
|
|||
|
|
- 同時接続数: 10,000+対応可能
|
|||
|
|
|
|||
|
|
## 🔍 検証方法
|
|||
|
|
|
|||
|
|
### 1. Coroutine統計
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"total_coroutines": 10000,
|
|||
|
|
"active_coroutines": 500,
|
|||
|
|
"yield_count": 50000,
|
|||
|
|
"average_yield_ns": 100,
|
|||
|
|
"stack_memory_mb": 10
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. ベンチマーク
|
|||
|
|
```bash
|
|||
|
|
# 10000並行リクエスト
|
|||
|
|
./target/release/nyash bench/async_stress.nyash
|
|||
|
|
|
|||
|
|
# メモリプロファイル
|
|||
|
|
NYASH_COROUTINE_STATS=1 ./target/release/nyash bench/async_memory.nyash
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🚧 実装上の注意点
|
|||
|
|
|
|||
|
|
1. **Stack管理**
|
|||
|
|
- Segmented stack対応
|
|||
|
|
- Stack overflow検出
|
|||
|
|
- GC root scanning
|
|||
|
|
|
|||
|
|
2. **エラー伝播**
|
|||
|
|
- async境界でのエラー
|
|||
|
|
- Promise rejection chain
|
|||
|
|
|
|||
|
|
3. **デバッグ対応**
|
|||
|
|
- async stack trace
|
|||
|
|
- Coroutine状態可視化
|
|||
|
|
|
|||
|
|
## 🎉 完了基準
|
|||
|
|
|
|||
|
|
- [ ] async/await構文実装
|
|||
|
|
- [ ] MIR coroutine変換
|
|||
|
|
- [ ] JIT最適化
|
|||
|
|
- [ ] Promise/PromiseBox実装
|
|||
|
|
- [ ] 10000並行処理ベンチマーク達成
|