diff options
| author | Somhairle H. Marisol <[email protected]> | 2026-09-29 03:45:58 +0800 |
|---|---|---|
| committer | Somhairle H. Marisol <[email protected]> | 2026-09-29 03:45:58 +0800 |
| commit | 2c559a79a5977c19876cee58d336b7983f67fa4c (patch) | |
| tree | 9607e336fc740bc840a02143f63fd7932aecedf3 /src/LivingVillage.Headless/Program.fs | |
| parent | eb4fca23cabce5762fc256a659228a5d50b5e9a9 (diff) | |
| download | living-village-2c559a79a5977c19876cee58d336b7983f67fa4c.tar.gz | |
m3r: 缩尺批验收口(50×15 流式 + systemd --user 单元 + 独立验收脚本)
缩尺口径:M3 phase2 50×100 实测 11.4h 仅完成 6/50(见
artifacts/m3-acceptance/final-candidate-20260921T095500Z/logs/*.diagnosis.txt),
改为 50 世界 × 15 模拟日、逐世界流式打印并 flush。标定据 docs/overnight-status.md
(Hermes 单 worker 亲测):1×2=34.6s、1×5=87.7s、1×10=173.6s,外推 15 天 ≈260s。
注意:派单所引用 artifacts/m3-independent/20260921T105500Z 全盘查找不存在,
故改用上述仓内标定,未编造任何数据。
内容:
- Headless 边缘:runSimulation 拆出 runSimulationCore 增加逐模拟日回调;
evalWorld 传入回调;runBatch 每 (days/5) 天打印 `[world k] progress day=d/D elapsed=..s`
并 flush,长世界不再零进度(BatchOutput.progressLine 纯函数 + 单测)。
- 新增 deploy/living-village-m3r.service 模板:wrapper 作 MasterProcess、
StandardOutput/StandardError=append: acceptance.log、Restart=no;
systemd-analyze --user verify 通过。本机 ~/.config/systemd/user 属外部目录受限,
实跑以等价属性的 systemd-run --user 瞬态单元 living-village-m3r.service 承载。
- scripts/m3r-master.sh:写 state.json(pid/unit/log/commit/workers/worlds/days/
起止/exit),exec 2>&1 运行 batch 并回传真实退出码。
- scripts/verify-m3r.py:独立可复跑,校验 state 字段、50 条 world 行(索引/seed/
ok 与 check_a/b/c 自洽)、summary passed+failed=50 与极值一致、抽查 3 行、
[done] 50 条、progress 行、M3_ACCEPTANCE=PASS 且 ratio_min>2.0。
本次实跑(commit 895d792):12 worker,wall=4316.0s,50/50 OK,
ratio_min=3.346 ratio_max=12.321 gini∈[0.412,0.548],relconn=30/18,
check_a/b/c 全 PASS,M3_ACCEPTANCE=PASS,exit=0;独立脚本复跑 M3R_VERIFY=PASS。
证据:artifacts/m3r-acceptance/acceptance.log + 20260928T183215Z/state.json。
回归:Desktop 303/303 + Kernel 119/119;--performance-baseline ×3
digest=953775FAEB2F… 不变,performance_determinism=PASS。
未改 Kernel/Sim;未 push。
Diffstat (limited to 'src/LivingVillage.Headless/Program.fs')
| -rw-r--r-- | src/LivingVillage.Headless/Program.fs | 19 |
1 files changed, 15 insertions, 4 deletions
diff --git a/src/LivingVillage.Headless/Program.fs b/src/LivingVillage.Headless/Program.fs index 23434d0..4bd6424 100644 --- a/src/LivingVillage.Headless/Program.fs +++ b/src/LivingVillage.Headless/Program.fs @@ -71,7 +71,7 @@ type RunStats = OutOfBounds: int64 World: World } -let runSimulation (verbose: bool) (days: int64) (seed: uint64) (npcCount: int) : RunStats = +let runSimulationCore (verbose: bool) (days: int64) (seed: uint64) (npcCount: int) (onSimDay: int64 -> unit) : RunStats = let totalTicks = days * ticksPerDay if verbose then printfn "living-village headless days=%d seed=%d npcs=%d ticks=%d tickrate=%d" days seed npcCount totalTicks ticksPerSecond @@ -102,6 +102,7 @@ let runSimulation (verbose: bool) (days: int64) (seed: uint64) (npcCount: int) : driver <- d world <- Sim.step { Input = input } world t <- t + 1L + if t % ticksPerDay = 0L then onSimDay (t / ticksPerDay) let nightNow = Sim.isNightTick t if nightNow then nightTicks <- nightTicks + 1L else dayTicks <- dayTicks + 1L for npc in world.Npcs do @@ -152,6 +153,9 @@ let runSimulation (verbose: bool) (days: int64) (seed: uint64) (npcCount: int) : OutOfBounds = outOfBounds World = world } +let runSimulation (verbose: bool) (days: int64) (seed: uint64) (npcCount: int) : RunStats = + runSimulationCore verbose days seed npcCount ignore + let dumpRelations (world: World) : unit = let m = Sim.relationMatrix world let n = Array2D.length1 m @@ -405,9 +409,9 @@ type WorldVerdict = Ratio: float Gini: float } -let evalWorld (days: int64) (k: int) : WorldVerdict = +let evalWorld (days: int64) (k: int) (onSimDay: int64 -> unit) : WorldVerdict = let seed = 42UL + uint64 k - let stats = runSimulation false days seed Sim.npcCount + let stats = runSimulationCore false days seed Sim.npcCount onSimDay let m = Sim.relationMatrix stats.World let n = Array2D.length1 m let mutable hasNonFinite = stats.NonFinite > 0L @@ -461,11 +465,18 @@ let runBatch (worlds: int) (days: int64) : int = let wall = System.Diagnostics.Stopwatch.StartNew() // 逐世界流式:每个世界一完成立刻打印该行(行尾追加 elapsed_s),再打印一行 // [done k/N] 进度,并 flush,使长批次不再整轮零进度。结果仍按 world 序号落数组。 + let progressGate = obj () + let progressEvery = if days <= 5L then 1L else (days + 4L) / 5L let results = LivingVillage.Headless.BatchOutput.runStreaming worlds workers (fun k -> let sw = System.Diagnostics.Stopwatch.StartNew() - let verdict = evalWorld days k + let verdict = + evalWorld days k (fun day -> + if day % progressEvery = 0L then + lock progressGate (fun () -> + printfn "%s" (LivingVillage.Headless.BatchOutput.progressLine k day days sw.Elapsed.TotalSeconds) + Console.Out.Flush())) sw.Stop() (verdict, sw.Elapsed.TotalSeconds)) (fun k (verdict, seconds) doneCount -> |
