summaryrefslogtreecommitdiff
path: root/src/LivingVillage.Headless
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-22 10:36:39 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-22 10:36:39 +0800
commit164f4749460a304c1aaad4900ef3facc29859eb0 (patch)
tree05794b5f853566d68ebefc822b3dce3b5f780ca3 /src/LivingVillage.Headless
parentb377e750a6a3f35c5d64a29f14ac3fbd87d4a752 (diff)
downloadliving-village-164f4749460a304c1aaad4900ef3facc29859eb0.tar.gz
perf(kernel): P27 第一步 — Rumor 长跑基准与修复前量化
新增 Kernel 层 Rumor 长跑基准(RumorBench.fs + headless --rumor-bench [days npcs chats_per_day]),把「聊天→生成/追溯谣言」路径从 Sim.step 的 NPC 快照分配解耦,隔离测量谣言工作集随世界年龄的分配/耗时;同 Config 的 retained_id_checksum 证明确定性。 复核 M3-1 前提:World.Rumors 已被 M6a(d94ec2d/cc850bf)按 3 天保留窗口收敛, 单日成本平稳(1/5/10 天 ≈18.7/18.1/19.8s、≈19GB/天),超线性项消失。 剩余 Rumor 成本 = 每次聊天的两条 O(N) 扫描 latestRumorFor/rumorIsDuplicate, 且 [<Struct>] NpcId/RumorId 用 = 比较会逐条装箱(Sim.chat 单独测得 N=16000 时 393,736 B/次、随 N 线性)。 修复前基准(30 NPC,--rumor-bench D 30 2000): 3d chat_calls=6000 retained=6000 alloc=930,716,456 ms=1626.7 30d chat_calls=60000 retained=8000 alloc=13,419,045,864 ms=12182.5 60d chat_calls=120000 retained=8000 alloc=27,297,668,608 ms=23769.7 (60d 工作集稳定 8000,不随年龄增长。) docs/维护说明.md 新增 P27 小节记录上述现状与量级。不改任何模拟语义。
Diffstat (limited to 'src/LivingVillage.Headless')
-rw-r--r--src/LivingVillage.Headless/Program.fs26
1 files changed, 25 insertions, 1 deletions
diff --git a/src/LivingVillage.Headless/Program.fs b/src/LivingVillage.Headless/Program.fs
index 89f4f26..7f5dcf5 100644
--- a/src/LivingVillage.Headless/Program.fs
+++ b/src/LivingVillage.Headless/Program.fs
@@ -518,6 +518,28 @@ let runCostProbe (ladder: int64 list) : int =
Console.Out.Flush()
0
+/// `--rumor-bench [days] [npcs] [chats_per_day]`:Kernel 层 Rumor 长跑基准
+/// (默认 30 NPC × 60 模拟日 × 每日 2000 次聊天)。只读、不改模拟语义。
+let runRumorBench (argv: string[]) : int =
+ let tryInt64 (index: int) =
+ if argv.Length > index then
+ match Int64.TryParse argv.[index] with
+ | true, value -> Some value
+ | _ -> None
+ else None
+ let days = tryInt64 1 |> Option.defaultValue RumorBench.defaultConfig.Days
+ let npcs = tryInt64 2 |> Option.defaultValue (int64 RumorBench.defaultConfig.NpcCount) |> int
+ let chatsPerDay = tryInt64 3 |> Option.defaultValue (int64 RumorBench.defaultConfig.ChatsPerDay) |> int
+ let config =
+ { RumorBench.defaultConfig with
+ Days = days
+ NpcCount = npcs
+ ChatsPerDay = chatsPerDay }
+ let result = RumorBench.run config
+ printfn "%s" (RumorBench.format result)
+ Console.Out.Flush()
+ 0
+
let runExisting (argv: string[]) =
let rec parse
(i: int)
@@ -581,7 +603,7 @@ let runExisting (argv: string[]) =
if stats.NonFinite > 0L || stats.OutOfBounds > 0L || not replayOk then 1 else 0
| Error msg ->
eprintfn $"headless: {msg}"
- eprintfn "usage: dotnet run -c Release --project src/LivingVillage.Headless -- --days N --seed S [--npc N] [--dump-relations] [--dump-rumors] [--replay-rumors] | --m5-smoke | --m6a-smoke | --batch K D | --performance-baseline | --cost-probe [D1,D2,...]"
+ eprintfn "usage: dotnet run -c Release --project src/LivingVillage.Headless -- --days N --seed S [--npc N] [--dump-relations] [--dump-rumors] [--replay-rumors] | --m5-smoke | --m6a-smoke | --batch K D | --performance-baseline | --cost-probe [D1,D2,...] | --rumor-bench [days npcs chats_per_day]"
2
let private parseCostLadder (argv: string[]) : int64 list =
@@ -608,5 +630,7 @@ let main argv =
LivingVillage.Headless.PerformanceProbe.runDefault ()
elif argv.Length >= 1 && argv.[0] = "--cost-probe" then
runCostProbe (parseCostLadder argv)
+ elif argv.Length >= 1 && argv.[0] = "--rumor-bench" then
+ runRumorBench argv
else
runExisting argv