SlimePython PoC 0–11 ― Hybrid Bit-Exact Isolate 実証 deep-dive
実機(WSL2 / CPython 3.12.3 / RustPython 0.5.0 / wasmtime 40 / x86_64)で行った PoC 0–11 の実ソース・変換コード・実測・再現手順です。数字はすべて固定 CPython・同一マシンでの実測値。AI 罠 17(empirical claim 捏造)回避方針に従い、未測定範囲は 100% と主張しません。
0. モデル(2 領域 + 2 tier)
| 領域 | 変換先 | 保証 |
|---|---|---|
| 静的領域 | Slot IR → 純 Rust | 構造的に bit-exact |
| 動的領域 | Python 断片のまま Deterministic CPython Isolate へ委譲 | 同一環境で bit-exact(矛盾は保存、reject は非決定性のみ) |
| tier | 実装 | 忠実度 | 速度 | 移植 |
|---|---|---|---|---|
| Full | CPython 静的 Isolate | 40/40 bit-exact | python3 同等以上 | x86_64/glibc |
| Light | RustPython → WASM | 19/20(差は例外文面) | ~3–6× | WASM/aarch64 へ移植可 |
1. Isolate runner(全 PoC 共通・~20 行)
動的 Python 断片を、決定論制約下の埋め込み CPython にそのまま渡して実行。stdout を SHA-256 で python3 と突き合わせる。
// PoC 2/3 Isolate runner — PYTHONHASHSEED=0 固定、.py を埋込 CPython で実行
use pyo3::prelude::*;
fn main() {
std::env::set_var("PYTHONHASHSEED", "0");
std::env::set_var("PYTHONUNBUFFERED", "1");
let path = std::env::args().nth(1).unwrap();
let code = std::fs::read_to_string(&path).unwrap();
Python::with_gil(|py| {
if let Err(e) = py.run_bound(&code, None, None) { e.print(py); std::process::exit(1); }
let _ = py.run_bound("import sys\nsys.stdout.flush()", None, None);
});
}
動的サンプル例(§13 で reject だった構文も bit-exact)
# monkey patch + decorator + kwargs forwarding(PoC 2 #15) def add_log(fn): def wrapped(*args, **kwargs): return f"LOG:{fn(*args, **kwargs)}" return wrapped class Service: def compute(self, x): return x * 100 Service.compute = add_log(Service.compute) # 実行時クラス書換 print(Service().compute(7)) # LOG:700 — Python=Isolate で SHA-256 一致
2. 正しさ ― 累計 40/40 bit-exact
| PoC | 内容 | 結果 |
|---|---|---|
| 1 | Any 5 | 5/5 |
| 2 | *args/**kwargs + 動的 getattr/setattr + monkey 15 | 15/15 |
| 3 | 巷の実コード動的 20(metaclass / 記述子 / dataclass / generator / eval-exec …) | 20/20 |
3. Full tier 速度 ― 「遅い」はビルド構成の問題だった(PoC 4→6)
当初、埋め込みは hot loop で python3 より ~18–38% 遅かった(PoC 4 のクロスオーバー)。根本原因は 共有 libpython.so の PIC(GOT/PLT 間接化)。/usr/bin/python3 は libpython を静的埋め込み(非 PIC)していた(ldd に libpython 行なし)。
# PoC 6: ソースから静的ビルド = production レシピ(スタブ不要・PIE/ASLR 維持) ./configure --prefix=<pfx> --disable-shared --enable-optimizations --with-lto make -j$(nproc) && make altinstall # Isolate を静的 libpython にリンク(PyO3 shared=false / prepare_freethreaded_python 手動) PYO3_CONFIG_FILE=pyo3-source.cfg LIBPY_KIND=source cargo build --release
| M(work/launch) | iso_source / python3 |
|---|---|
| 1(短命) | 0.60×(速い) |
| 1,000,000(hot) | 0.99×(同等) |
共有 .so のみ 1.19–1.34× で遅い ― これを Py_BytesMain ランナー(CLI 完全同一経路)+ distro の非PIC/PIC/.so 3 variant で切り分け確定(PoC 4.5/5)。
4. Light tier ― RustPython → WASM(PoC 7/8)
同 20 サンプルで 19/20 bit-exact(唯一の差は abc 例外メッセージ文面、意味論は一致)。wasm32-wasip1 にクロスコンパイルし wasmtime で実行 ― wasm = native RustPython 20/20。CPython 埋め込みが不能な WASI を Light tier が完動。
5. 自動振り分け + 実行時プロファイル(PoC 9/10)
# PoC 9 router(静的): reject は非決定性のみ。誤ルート 0/20
if target in ("wasm","aarch64"): return Decision("light", "Full 不能")
if sig.unsupported: return Decision("full", "非対応")
if sig.exc_text_dep and exact_exceptions: return Decision("full", "例外文面の完全一致")
if sig.hot_loop and perf == "fast": return Decision("full", "hot loop 回避")
return Decision("light", "Light で十分")
PoC 10: 静的に出ない計算重(range(変数)・再帰)を実行時プロファイルで捕捉 → 次回 Full へ。hot_varrange Light 633ms→Full 212ms(3×)、hot_fib 1260ms→135ms(9×)。
6. reject 対象 = 非決定性のみ(PoC 11 実測 4 クラス)
| クラス | 例 | 扱い |
|---|---|---|
| A 既定で保全 | hash/set順(seed0)・dict・float演算・GIL threading | そのまま bit-exact |
| B Isolate注入で保全 | seedなし random・time | seed/clock 固定で決定論(値は正規化) |
| C 同一環境のみ保全 | os.environ・file I/O・subprocess | 同一環境では一致、環境間は CI 範囲 |
| D 真に非決定論 | id()・default repr(アドレス)・getpid・uuid4 | 保証対象外(= Python の性) |
「矛盾は保存、reject は非決定性のみ」。保全不能の核はメモリアドレス・プロセス ID・OS エントロピーの 3 種だけ。
7. ダウンロード(再現一式)
再現には CPython 3.12 / Rust 1.93 / PyO3 0.22 / RustPython 0.5 / wasmtime が必要。各 PoC ディレクトリの run_*.sh 参照。質問は お問い合わせへ。
