Spaces:
Sleeping
Sleeping
バイトコード拡張案の分析: 5x32 vs 64bit vs 現状維持
概要
ユーザーからの技術的異議を受けて、3つのアプローチを比較検討します:
- 現状維持 (4x32):
[op, v, a, s]- 4整数 x 32ビット - 5x32拡張:
[op, v, s, a_low, a_high]- 5整数 x 32ビット - 64bit拡張:
[op, v, a64, s]- aのみ64ビット
1. ユーザーの懸念点 (正当性の評価)
1.1 v への Color Filter 移動の問題
| 懸念 | 評価 | 理由 |
|---|---|---|
| Card ID > 65535 での破損 | ✅ 正当 | プロモカード等で ID が 70050+ になる可能性あり |
| AI学習の複雑化 | ✅ 正当 | 「opcode が X の場合は v から色を探す」という条件分岐は NN に負担 |
1.2 Assembly-Style Unrolling の問題
| 懸念 | 評価 | 理由 |
|---|---|---|
| MCTS深度爆発 | ✅ 正当 | 1効果 = 3-4ステップ は状態空間を不必要に拡大 |
| 部分状態の探索 | ✅ 正当 | 「フィルタ済みだが選択未完了」の中間状態が増加 |
1.3 拡張性の欠如
| 懸念 | 評価 | 理由 |
|---|---|---|
| 将来のメカニクス対応 | ✅ 正当 | 学年フィルタ、ブレードサイズ等の新規フィルタで即座に限界 |
2. パフォーマンス影響の分析
2.1 Rust での影響
現状 (4x32)
// bytecode: Vec<i32>
for chunk in bytecode.chunks(4) {
let (op, v, a, s) = (chunk[0], chunk[1], chunk[2], chunk[3]);
// 処理
}
5x32拡張
// bytecode: Vec<i32>
for chunk in bytecode.chunks(5) {
let (op, v, s, a_low, a_high) = (chunk[0], chunk[1], chunk[2], chunk[3], chunk[4]);
let a = ((a_high as u64) << 32) | (a_low as u64);
// 処理
}
影響:
- メモリ: +25% 増加
- キャッシュミス率: 若干増加 (1命令あたり 20bytes → 25bytes)
- CPUサイクル: ほぼ変わらず (ビットシフトは高速)
64bit拡張
// bytecode: Vec<i64> または混合型
for chunk in bytecode.chunks(4) {
let (op, v, a, s) = (chunk[0] as i32, chunk[1] as i32, chunk[2], chunk[3] as i32);
// a は i64
}
影響:
- メモリ: +25% 増加 (4x32=128bit → 2x32+1x64+1x32=160bit)
- キャッシュミス率: 若干増加
- CPUサイクル: ほぼ変わらず (64bit演算は現代CPUで高速)
2.2 WGSL (GPU) での影響
現状 (4x32)
@group(0) @binding(2) var<storage, read> bytecode: array<i32>;
fn get_instruction(ip: u32) -> vec4<i32> {
return vec4<i32>(
bytecode[ip],
bytecode[ip + 1u],
bytecode[ip + 2u],
bytecode[ip + 3u]
);
}
5x32拡張
@group(0) @binding(2) var<storage, read> bytecode: array<i32>;
fn get_instruction(ip: u32) -> vec5<i32> { // vec5 は存在しない!
// WGSL には vec5 がないため、個別にロードが必要
let op = bytecode[ip];
let v = bytecode[ip + 1u];
let s = bytecode[ip + 2u];
let a_lo = bytecode[ip + 3u];
let a_hi = bytecode[ip + 4u];
let a = (u32(a_lo) | (u32(a_hi) << 32u)); // u64 として結合
// ...
}
影響:
- 重大: WGSL に
vec5が存在しないため、個別ロードが必要 - メモリアクセス: 5回/命令 (現状は4回)
- レジスタ圧力: 若干増加
64bit拡張
@group(0) @binding(2) var<storage, read> bytecode: array<i64>; // または混合
fn get_instruction(ip: u32) -> vec4<i64> { // 一部が i64
// WGSL は型統一が必要
return vec4<i64>(
bytecode[ip],
bytecode[ip + 1u],
bytecode[ip + 2u],
bytecode[ip + 3u]
);
}
影響:
- WGSL は
array<i64>をサポート - ただし、
op,v,sも 64bit になるためメモリ無駄 - 混合型は WGSL で複雑化
3. 推奨アプローチ: 5x32 拡張
3.1 理由
| 基準 | 4x32 (現状) | 5x32 | 64bit |
|---|---|---|---|
| メモリ効率 | ✅ 最良 | 🟡 +25% | 🟡 +25% |
| WGSL互換性 | ✅ 最良 | 🟡 個別ロード必要 | 🟡 型統一必要 |
| 拡張性 | ❌ 不十分 | ✅ 64bit属性 | ✅ 64bit属性 |
| AI学習 | ❌ 複雑な条件 | ✅ クリーン | ✅ クリーン |
| 実装コスト | - | 🟡 中程度 | 🔴 高い |
3.2 新しいレイアウト (5x32)
[op, v, s, a_low, a_high]
op (i32): Opcode
v (i32): Value/Count (16bit) + Color Mask (6bit) + Flags (10bit)
s (i32): Slot/Zone (24bit) + Flags (8bit)
a (i64): Attribute Filter (64bit完全使用可能)
3.3 64bit a ワードの新しいレイアウト
┌─────────────────────────────────────────────────────────────────┐
│ Bit 0 : IS_OPTIONAL │
│ Bit 1 : DYNAMIC_VALUE │
│ Bits 2-3 : Card Type (0=Any, 1=Member, 2=Live) │
│ Bits 4-7 : Group ID (4 bits = 16 groups) │
│ Bits 8-12 : Unit ID (5 bits = 32 units) │
│ Bits 13-17 : Cost Threshold (5 bits = 0-31) │
│ Bit 18 : Cost Mode (0=GE, 1=LE) │
│ Bit 19 : Tapped Filter │
│ Bit 20 : Has Blade Heart │
│ Bit 21 : Not Has Blade Heart │
│ Bits 22-28 : Color Mask (7 bits = 7 colors) │
│ Bits 29-35 : Character ID 1 (7 bits = 128 chars) │
│ Bits 36-42 : Character ID 2 (7 bits) │
│ Bits 43-49 : Character ID 3 (7 bits) │
│ Bits 50-56 : Special Filter ID (7 bits) │
│ Bits 57-63 : Reserved for future use │
└─────────────────────────────────────────────────────────────────┘
改善点:
- Color Mask が
a内に配置 (v から分離) - Character ID が 64bit 内に収まる (切り捨て問題解決)
- 将来の拡張用に 7ビット予備
4. 実装ステップ
Phase 1: Rust インタープリタの更新
バイトコードストレージの変更
// Before pub bytecode: Vec<i32> // After pub bytecode: Vec<i32> // 5要素ごとに1命令チャンク処理の変更
// Before for chunk in bytecode.chunks(4) { ... } // After for chunk in bytecode.chunks(5) { let op = chunk[0]; let v = chunk[1]; let s = chunk[2]; let a = if chunk.len() >= 5 { ((chunk[4] as u64) << 32) | (chunk[3] as u64) } else { 0 }; // ... }
Phase 2: Python コンパイラの更新
compile()メソッドの変更def compile(self) -> List[int]: bytecode = [] # ... 既存のロジック ... # 各命令に a_high を追加 for i in range(0, len(bytecode), 4): a = bytecode[i+2] a_low = a & 0xFFFFFFFF a_high = (a >> 32) & 0xFFFFFFFF bytecode[i+2] = a_low bytecode.insert(i+3, a_high) return bytecode
Phase 3: WGSL シェーダーの更新
- 命令読み込みの変更
fn get_instruction(ip: u32) -> (i32, i32, i32, u64) { let op = bytecode[ip]; let v = bytecode[ip + 1u]; let s = bytecode[ip + 2u]; let a_lo = bytecode[ip + 3u]; let a_hi = bytecode[ip + 4u]; let a = (u32(a_lo) | (u32(a_hi) << 32u)); return (op, v, s, a); }
5. パフォーマンス予測
5.1 メモリ使用量
| 項目 | 現状 (4x32) | 5x32 | 増加率 |
|---|---|---|---|
| 1命令あたり | 16 bytes | 20 bytes | +25% |
| 全カードのバイトコード | ~1.5 MB | ~1.9 MB | +25% |
| GPU VRAM | ~2 MB | ~2.5 MB | +25% |
5.2 実行速度
| 環境 | 予測影響 |
|---|---|
| Rust (CPU) | < 1% 遅延 (メモリ帯域幅依存) |
| WGSL (GPU) | 1-3% 遅延 (メモリアクセス増加) |
| AI訓練 | 改善 (状態空間の削減) |
5.3 AI訓練への影響
| 側面 | 影響 |
|---|---|
| 状態空間 | 削減 (Unrolling回避) |
| MCTS深度 | 削減 (1効果 = 1ステップ) |
| NN入力エンコーディング | 簡素化 (レジスタ意味が明確) |
6. 結論
推奨: 5x32 拡張アプローチ
理由:
- 64bit 属性フィルタで全ての情報を1命令に収容
- AI訓練にとって最適 (状態空間削減)
- 実装コストが中程度
- 将来の拡張性を確保
次のステップ:
- ユーザーの承認
- Codeモードでの実装開始