Skip to content

blockData vs IndexedDB_Memory_Storage 谁适合作为权威数据源 #74

Description

@jayli

从consolidation 的时候 blockData 被多次深拷贝带来的卡顿的问题分析到了权威数据源的多次全量拷贝带来的设计问题,接下来是深入的分析。

代码基线:

1ed3544

Trace 分析结论:consolidation 触发的持久化深拷贝是卡顿的主因

通过 Chrome Trace 分析,定位到一次删除方块后触发 consolidation 的完整卡顿链路:

单帧卡顿 1422ms

一个动画帧耗时 1422ms(正常 ≤16ms),FunctionCall 事件分解如下:

耗时 位置 说明
1422ms Game.js:425 游戏主循环帧(总)
392ms IndexedDBUtils.js:58 request.onsuccess IndexedDB 事务提交 + 内部 structuredClone
376ms IndexedDBUtils.js:58 request.onsuccess 同上
358ms WorkerRpcClient.js:15 worker.onmessage Worker 返回 consolidation 结果后主线程处理
358ms PersistenceWorker.js:343 self.onmessage PersistenceWorker 处理 saveRegionRecord
172ms PersistenceWorker.js:343 self.onmessage 同上

根因:blockData 被多次 structuredClone

在 consolidation → 持久化链路中,同一份 blockData 被克隆了 3~4 次

主线程 WorldRuntime._flushDirtyChunks()
  │
  ├─ ① _serializeBlockData()           ← Map → 普通对象转换
  │
  └─ postMessage('applyRegionPatch')
       │
       └─ ② 浏览器内部 structuredClone  ← postMessage 跨 Worker 传递
            │
            PersistenceWorker.self.onmessage (358ms)
              │
              ├─ ③ structuredClone(region)  ← PersistenceWorker.js:235 显式深克隆
              │    整份 region 对象,含所有 chunk 的 blockData
              │
              └─ saveRegionRecord → store.put({ data: record })
                   │
                   └─ ④ IndexedDB 内部 structuredClone ← 规范要求存储隔离

为什么明明是内存存储还这么慢?

不是磁盘 I/O,是 structuredClone 的 CPU 开销。structuredClone 对大型嵌套对象是 O(n) 的遍历和复制操作。一个 region 包含多个 chunk,每个 chunk 的 blockData 有几百到上千个坐标映射,clone 时每个 key-value 都要递归处理。

IndexedDB 规范要求在存储前对数据进行结构化克隆以保障数据隔离,所以即使你把数据存在内存里(无磁盘延迟),store.put(largeObject) 仍然会触发一次完整的 structuredClone。

优化方向

  1. 去掉 PersistenceWorker.js:235 的显式 structuredClone(P0)—— IndexedDB 内部本身就会 clone,显式 clone 是纯浪费。改为原地修改 region 缓存,写入失败再从 IndexedDB 回读恢复
  2. _flushDirtyChunks 的刷写与 consolidation 错峰(P0)—— consolidation 完成后延迟再触发持久化,避免两者抢 CPU
  3. 考虑使用 Transferable 传递 blockData(P1)—— postMessage 时用 transfer list 转移 ArrayBuffer 所有权,避免跨线程 clone
  4. 将大 region 粒度拆分(P2)—— 减少单次 store.put 的数据量

为此我想将权威数据源统一到 blockData 身上,写了一个方案:

https://gist.github.com/jayli/da68916e68272032abe7cc7e79b5a146

还没有详细review这个方案的合理性

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions