diff options
| author | Somhairle H. Marisol <[email protected]> | 2026-09-28 21:54:17 +0800 |
|---|---|---|
| committer | Somhairle H. Marisol <[email protected]> | 2026-09-28 21:54:17 +0800 |
| commit | 1f75c8ad138f252c20244e9b690e3cc0ef32bcde (patch) | |
| tree | 5c7712f3205598351abe0deca761d447c87f4569 | |
| parent | 576ffc44bd94cc5ba7fa67bf9e1d71b8c9ba2979 (diff) | |
| download | living-village-1f75c8ad138f252c20244e9b690e3cc0ef32bcde.tar.gz | |
p49: 长局内存 profile 利基 --profile-long-run(逐日采样 RSS + Events/Rumors/Annals/Memory 条目数与估算字节)
| -rw-r--r-- | src/LivingVillage.Headless/Program.fs | 63 |
1 files changed, 63 insertions, 0 deletions
diff --git a/src/LivingVillage.Headless/Program.fs b/src/LivingVillage.Headless/Program.fs index 7f5dcf5..23434d0 100644 --- a/src/LivingVillage.Headless/Program.fs +++ b/src/LivingVillage.Headless/Program.fs @@ -540,6 +540,67 @@ let runRumorBench (argv: string[]) : int = Console.Out.Flush() 0 +/// `--profile-long-run [days] [sampleEveryDays] [npcs] [seed]`:长局内存 profile(只读,不改模拟行为)。 +/// 逐模拟日推进,每 `sampleEveryDays` 天采样并流式打印+flush: +/// * RSS:GC.GetTotalMemory(true)(GC live set)与 Process.WorkingSet64(OS RSS); +/// * 三类历史集合 Events / Rumors / Annals 的条目数与估算字节; +/// * NPC Mind.Memory 总条目数与估算字节; +/// * 首个与最后一个采样点附带世界 digest,证明确定性不被 profile 影响。 +/// 估算字节为保守常数模型(InteractionEvent≈48B、RumorEvent≈112B、AnnalEntry≈96B+Summary*2、 +/// MemoryEvent≈72B),仅作数量级参考;ground truth 是 GC live set 与 WorkingSet。 +let runProfileLongRun (argv: string[]) : int = + let argInt64 index (dflt: int64) = + if argv.Length > index then + match Int64.TryParse argv.[index] with + | true, v when v > 0L -> v + | _ -> dflt + else dflt + let days = argInt64 1 40L + let sampleEvery = argInt64 2 4L + let npcs = int (argInt64 3 (int64 Sim.npcCount)) + let seed = if argv.Length > 4 then (match UInt64.TryParse argv.[4] with | true, v -> v | _ -> 42UL) else 42UL + let mb (bytes: int64) = float bytes / (1024.0 * 1024.0) + let proc = System.Diagnostics.Process.GetCurrentProcess() + printfn "long_run_start days=%d sample_every_days=%d npcs=%d seed=%d ticks_per_day=%d" days sampleEvery npcs seed ticksPerDay + Console.Out.Flush() + let mutable world = Sim.initialWorldN seed npcs + let mutable driver = { Rng = Rng.ofSeed seed; Until = segmentTicks; Input = zeroInput } + let sw = System.Diagnostics.Stopwatch.StartNew() + let sample (day: int64) (withDigest: bool) = + let gcLive = System.GC.GetTotalMemory true + proc.Refresh() + let ws = proc.WorkingSet64 + let events = world.Events.Length + let rumors = world.Rumors.Length + let annals = world.Annals.Length + let memEntries = world.Npcs |> Array.sumBy (fun n -> n.Mind.Memory.Length) + let annalsBytes = world.Annals |> List.sumBy (fun a -> 96 + a.Summary.Length * 2) + let digest = if withDigest then LivingVillage.Headless.PerformanceProbe.worldDigest world else "-" + printfn + "long_run_sample day=%d tick=%d gc_live_mb=%.2f working_set_mb=%.2f events=%d rumors=%d annals=%d memory_entries=%d est_events_kb=%.1f est_rumors_kb=%.1f est_annals_kb=%.1f est_memory_kb=%.1f elapsed_s=%.1f digest=%s" + day world.Tick (mb gcLive) (mb ws) events rumors annals memEntries + (float (events * 48) / 1024.0) (float (rumors * 112) / 1024.0) (float annalsBytes / 1024.0) + (float (memEntries * 72) / 1024.0) sw.Elapsed.TotalSeconds digest + Console.Out.Flush() + sample 0L true + let mutable dayNo = 0L + while dayNo < days do + dayNo <- dayNo + 1L + let dayEnd = dayNo * ticksPerDay + while world.Tick < dayEnd do + let d, input = + if world.Tick < driver.Until then driver, driver.Input + else + let d = nextSegment driver.Rng driver.Until + d, d.Input + driver <- d + world <- Sim.step { Input = input } world + if dayNo % sampleEvery = 0L || dayNo = days then sample dayNo false + let finalDigest = LivingVillage.Headless.PerformanceProbe.worldDigest world + printfn "long_run_done days=%d tick=%d final_digest=%s elapsed_s=%.1f" dayNo world.Tick finalDigest sw.Elapsed.TotalSeconds + Console.Out.Flush() + if world.Tick = days * ticksPerDay then 0 else 1 + let runExisting (argv: string[]) = let rec parse (i: int) @@ -632,5 +693,7 @@ let main argv = runCostProbe (parseCostLadder argv) elif argv.Length >= 1 && argv.[0] = "--rumor-bench" then runRumorBench argv + elif argv.Length >= 1 && argv.[0] = "--profile-long-run" then + runProfileLongRun argv else runExisting argv |
