200 lines
5.2 KiB
Markdown
200 lines
5.2 KiB
Markdown
|
|
# Phase 11.5a: Write Barrier除去によるGC最適化
|
|||
|
|
|
|||
|
|
## 🎯 目標
|
|||
|
|
JITコンパイル時にescape analysisを行い、不要なwrite barrierを除去してGC性能を大幅に向上させる。
|
|||
|
|
|
|||
|
|
## 📊 現状の問題
|
|||
|
|
|
|||
|
|
### 現在のVM実装
|
|||
|
|
```rust
|
|||
|
|
// すべてのrefset操作でbarrierが呼ばれる
|
|||
|
|
pub fn execute_ref_set(&mut self, reference: ValueId, field: &str, value: ValueId)
|
|||
|
|
-> Result<ControlFlow, VMError> {
|
|||
|
|
// ... 値の設定 ...
|
|||
|
|
|
|||
|
|
// 常にwrite barrierが実行される
|
|||
|
|
gc_write_barrier_site(&self.runtime, "RefSet");
|
|||
|
|
Ok(ControlFlow::Continue)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### オーバーヘッド
|
|||
|
|
- 単純な代入でも毎回barrier呼び出し
|
|||
|
|
- Stack上のローカル変数でも不要にbarrier
|
|||
|
|
- ループ内での大量のbarrier呼び出し
|
|||
|
|
|
|||
|
|
## 🚀 実装計画
|
|||
|
|
|
|||
|
|
### Step 1: Escape Analysis基盤
|
|||
|
|
```rust
|
|||
|
|
// mir/escape_analysis.rs
|
|||
|
|
pub struct EscapeAnalysis {
|
|||
|
|
// allocation site追跡
|
|||
|
|
allocations: HashMap<ValueId, AllocInfo>,
|
|||
|
|
// escape状態
|
|||
|
|
escapes: HashSet<ValueId>,
|
|||
|
|
// 解析結果キャッシュ
|
|||
|
|
cache: HashMap<FunctionId, EscapeInfo>,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[derive(Debug)]
|
|||
|
|
struct AllocInfo {
|
|||
|
|
location: BasicBlockId,
|
|||
|
|
kind: AllocKind,
|
|||
|
|
size: Option<usize>,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
enum AllocKind {
|
|||
|
|
NewBox, // new StringBox()
|
|||
|
|
ArrayNew, // []
|
|||
|
|
RefNew, // ユーザー定義Box
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Step 2: MIR解析
|
|||
|
|
```rust
|
|||
|
|
impl EscapeAnalysis {
|
|||
|
|
/// 関数内でのescape解析
|
|||
|
|
pub fn analyze_function(&mut self, func: &MirFunction) -> EscapeInfo {
|
|||
|
|
// 1. allocation site収集
|
|||
|
|
for (bb_id, bb) in &func.basic_blocks {
|
|||
|
|
for inst in &bb.instructions {
|
|||
|
|
match inst {
|
|||
|
|
MirInstruction::NewBox { dst, .. } |
|
|||
|
|
MirInstruction::ArrayNew { dst, .. } |
|
|||
|
|
MirInstruction::RefNew { dst, .. } => {
|
|||
|
|
self.allocations.insert(*dst, AllocInfo {
|
|||
|
|
location: bb_id,
|
|||
|
|
kind: self.classify_alloc(inst),
|
|||
|
|
size: self.estimate_size(inst),
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
_ => {}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2. escape point検出
|
|||
|
|
self.find_escape_points(func);
|
|||
|
|
|
|||
|
|
// 3. 結果集計
|
|||
|
|
EscapeInfo {
|
|||
|
|
non_escaping: self.collect_non_escaping(),
|
|||
|
|
barrier_sites: self.collect_barrier_sites(),
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fn find_escape_points(&mut self, func: &MirFunction) {
|
|||
|
|
// return文でのescape
|
|||
|
|
// 関数引数としてのescape
|
|||
|
|
// グローバル変数へのescape
|
|||
|
|
// プラグイン呼び出しでのescape
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Step 3: JIT統合
|
|||
|
|
```rust
|
|||
|
|
// jit/lower/builder.rs
|
|||
|
|
impl<'a> LoweringBuilder<'a> {
|
|||
|
|
fn emit_ref_set(&mut self, reference: Value, field: &str, value: Value) {
|
|||
|
|
// escape解析結果を確認
|
|||
|
|
let needs_barrier = self.escape_info
|
|||
|
|
.map(|info| info.needs_barrier(reference))
|
|||
|
|
.unwrap_or(true); // 解析なしなら保守的にbarrier
|
|||
|
|
|
|||
|
|
if needs_barrier {
|
|||
|
|
// barrierあり
|
|||
|
|
self.emit_gc_barrier(BarrierKind::Write);
|
|||
|
|
} else {
|
|||
|
|
// barrier除去!
|
|||
|
|
if self.config.trace_opt {
|
|||
|
|
eprintln!("[JIT] barrier removed at {:?}", self.current_location());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 実際のstore操作
|
|||
|
|
self.emit_store(reference, field, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Step 4: 最適化レベル設定
|
|||
|
|
```rust
|
|||
|
|
// 環境変数で制御
|
|||
|
|
NYASH_JIT_ESCAPE_ANALYSIS=1 # escape analysis有効化
|
|||
|
|
NYASH_JIT_BARRIER_OPT=1 # barrier最適化有効化
|
|||
|
|
NYASH_JIT_BARRIER_STATS=1 # 統計出力
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 📈 期待される効果
|
|||
|
|
|
|||
|
|
### ベンチマーク例
|
|||
|
|
```nyash
|
|||
|
|
// 大量のローカル変数操作
|
|||
|
|
function processData(n) {
|
|||
|
|
local sum = 0
|
|||
|
|
local temp = new MapBox()
|
|||
|
|
|
|||
|
|
loop(i < n) {
|
|||
|
|
temp.set(i, i * 2) // escape analysisでbarrier除去
|
|||
|
|
sum = sum + temp.get(i)
|
|||
|
|
i = i + 1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return sum
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 性能改善予測
|
|||
|
|
- ローカル変数操作: 90%以上のbarrier除去
|
|||
|
|
- ループ内操作: 80%以上の高速化
|
|||
|
|
- 全体的なGCオーバーヘッド: 50%削減
|
|||
|
|
|
|||
|
|
## 🔍 検証方法
|
|||
|
|
|
|||
|
|
### 1. Barrier統計
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"total_barriers": 10000,
|
|||
|
|
"removed_barriers": 8500,
|
|||
|
|
"removal_rate": 0.85,
|
|||
|
|
"sites": {
|
|||
|
|
"RefSet": { "total": 5000, "removed": 4800 },
|
|||
|
|
"ArraySet": { "total": 3000, "removed": 2500 },
|
|||
|
|
"MapSet": { "total": 2000, "removed": 1200 }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 性能測定
|
|||
|
|
```bash
|
|||
|
|
# barrier最適化なし
|
|||
|
|
NYASH_JIT_ESCAPE_ANALYSIS=0 ./target/release/nyash --benchmark
|
|||
|
|
|
|||
|
|
# barrier最適化あり
|
|||
|
|
NYASH_JIT_ESCAPE_ANALYSIS=1 ./target/release/nyash --benchmark
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🚧 実装上の注意点
|
|||
|
|
|
|||
|
|
1. **保守的な解析**
|
|||
|
|
- 不明な場合は必ずbarrierを残す
|
|||
|
|
- プラグイン境界では常にbarrier
|
|||
|
|
|
|||
|
|
2. **デバッグ性**
|
|||
|
|
- 除去したbarrierサイトを記録
|
|||
|
|
- GCエラー時の診断情報
|
|||
|
|
|
|||
|
|
3. **段階的実装**
|
|||
|
|
- まずローカル変数のみ
|
|||
|
|
- 次にループ不変式
|
|||
|
|
- 最後に関数間解析
|
|||
|
|
|
|||
|
|
## 🎉 完了基準
|
|||
|
|
|
|||
|
|
- [ ] Escape analysis基本実装
|
|||
|
|
- [ ] MIR解析パス追加
|
|||
|
|
- [ ] JIT統合
|
|||
|
|
- [ ] ベンチマーク50%改善
|
|||
|
|
- [ ] ドキュメント更新
|