5.2 KiB
5.2 KiB
Phase 11.5a: Write Barrier除去によるGC最適化
🎯 目標
JITコンパイル時にescape analysisを行い、不要なwrite barrierを除去してGC性能を大幅に向上させる。
📊 現状の問題
現在のVM実装
// すべての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基盤
// 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解析
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統合
// 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: 最適化レベル設定
// 環境変数で制御
NYASH_JIT_ESCAPE_ANALYSIS=1 # escape analysis有効化
NYASH_JIT_BARRIER_OPT=1 # barrier最適化有効化
NYASH_JIT_BARRIER_STATS=1 # 統計出力
📈 期待される効果
ベンチマーク例
// 大量のローカル変数操作
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統計
{
"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. 性能測定
# barrier最適化なし
NYASH_JIT_ESCAPE_ANALYSIS=0 ./target/release/nyash --benchmark
# barrier最適化あり
NYASH_JIT_ESCAPE_ANALYSIS=1 ./target/release/nyash --benchmark
🚧 実装上の注意点
-
保守的な解析
- 不明な場合は必ずbarrierを残す
- プラグイン境界では常にbarrier
-
デバッグ性
- 除去したbarrierサイトを記録
- GCエラー時の診断情報
-
段階的実装
- まずローカル変数のみ
- 次にループ不変式
- 最後に関数間解析
🎉 完了基準
- Escape analysis基本実装
- MIR解析パス追加
- JIT統合
- ベンチマーク50%改善
- ドキュメント更新