这份速查怎么用
本卷 18 章会一路往 helio 内核里加零件。零件多了以后,最常见的困惑不是「这个算法怎么实现」,而是「这个类型叫什么、在哪一章、签名长什么样」。这份附录就是为这种时刻准备的:把全书出现过的类型与关键 API 按模块铺开,每一行给签名、一句话说明、出自哪一章三样东西,读到哪一节想不起来,回头扫一眼即可。
用法上,它是查阅表不是教程:不要从第一行读到最后一行,而是带着问题跳——想找句柄就翻到「句柄与容器」,想找任务系统就翻到 src/jobs/。真正讲原理的内容在正文各节,本表只做索引与备忘。
模块与章节的对应关系如下:
| 模块 | 负责什么 | 主要章节 |
|---|---|---|
src/core/ | 内存、句柄、容器、数学、主循环 | 第 2、4–10 章 |
src/ecs/ | 实体、组件存储、查询、系统调度 | 第 11–13 章 |
src/jobs/ | 工作者池、任务图、依赖 DAG | 第 14–15 章 |
src/reflect/ | 类型注册表、属性描述符、序列化 | 第 16–17 章 |
src/platform/ | 平台抽象层、日志、CVar、子系统编排 | 第 3 章 |
表中的「出处」列写的是章节号(如 6.2 指 6.2 节),而不是文件路径——因为同一份 API 可能被多节复用,章节号才是它「第一次被讲清楚」的地方。
src/core/ 内存、容器与数学
内核最底层的一层,也是全书代码量最大的一层。第 4 章讲分配器,第 5 章讲池化,第 6 章讲句柄,第 7 章讲自研容器,第 8–10 章讲数学与数值。
内存分配
| 名称 | 签名 / 成员 | 说明 | 出处 |
|---|---|---|---|
Arena | new Arena(capacityBytes) · alloc(bytes, align?) → offset · reset() · highWaterMark · usedBytes | 只前进不回收的线性分配器;帧末整体 reset 一次释放 | 4.2 |
ScratchArena | mark() → mark · release(mark?) · alloc(bytes, align?) | 帧内临时内存的栈式分配;mark / release 必须成对 | 4.2 |
ObjectPool<T> | new ObjectPool<T>({ create, reset, initial, policy? }) · acquire() → T · release(item) · stats → PoolStats | 定长对象池 + 空闲链表;stats.created 是收益指标 | 5.1 |
ObjectPoolOptions<T> | { create: () => T; reset: (item: T) => void; initial: number; policy?: 'throw' 或 'grow' } | 池的构造参数;create / reset 由调用方注入 | 5.1 |
PoolStats | { capacity, free, inUse, created, recycled, exhausted } | 池的运行统计 | 5.1 |
BlockAllocator | alloc(bytes) → ptr · free(ptr) · sizeClassOf(bytes) | 按 2 的幂分大小类,在固定 buffer 上切块 | 5.2 |
MemoryTag | tag(name) · bytesOf(name) · snapshot() | 按标签统计占用与分配计数,用于泄漏检测 | 5.3 |
alignUp | alignUp(n, align) → number | 向上对齐到 align 的整数倍 | 4.3 |
句柄与容器
| 名称 | 签名 / 成员 | 说明 | 出处 |
|---|---|---|---|
Handle | type Handle = number(低 20 位索引 + 高 12 位世代) | 可序列化的引用;失效可在访问瞬间检测 | 6.2 |
makeHandle | makeHandle(index, generation) → Handle | 把索引与世代拼成 32 位句柄 | 6.2 |
handleIndex | handleIndex(h) → number | 掩出低 20 位的槽位索引 | 6.2 |
handleGeneration | handleGeneration(h) → number | 右移掩出高 12 位的世代 | 6.2 |
NULL_HANDLE | NULL_HANDLE = 0 | 保留空句柄,永远取不到值 | 6.2 |
SlotMap<T> | insert(v) → Handle · get(h) → T 或 undefined · has(h) · expect(h) → T · remove(h) · entries() | 世代句柄容器;remove 时该槽位世代自增 | 6.2 |
SparseSet<T> | add(id, v) · get(id) · has(id) · remove(id) · values() · ids() · denseView() | 稀疏 ID → dense 下标;swap-remove 保持紧凑 | 6.3 |
DynamicArray<T> | push(v) · pop() · get(i) · set(i, v) · reserve(n) · length | 自增长数组;容量按 2 倍增长 | 7.1 |
OpenHashMap<K, V> | set(k, v) · get(k) · has(k) · delete(k) · loadFactor | 开放寻址哈希;线性探测与墓碑删除 | 7.1 |
BitSet | set(i) · clear(i) · test(i) · popcount() | 位集合,用位运算做成员测试 | 7.2 |
RingBuffer<T> | push(v) · pop() · peek() · full | 定长、零额外分配的环形缓冲 | 7.2 |
IntrusiveList | pushFront(node) · remove(node) · forEach(fn) | 侵入式链表,节点自带前驱后继指针 | 7.2 |
StringIntern | intern(s) → NameId · resolve(id) → string | 字符串驻留,把名字句柄化 | 7.3 |
Guid | parse(str) → Guid · toString() | 资源 GUID,稳定的资源标识 | 7.3 |
时间与主循环
| 名称 | 签名 / 成员 | 说明 | 出处 |
|---|---|---|---|
Clock | nowMs() → number | 单调毫秒时钟抽象;所有时间都从这里取 | 2.1 |
FakeClock | new FakeClock(startMs?) · advance(deltaMs) | 假时钟,用于测试与确定性回放 | 2.1 |
FrameTimer | begin() · end() → ms · avgMs() · percentile(p) · maxMs() · fps() · stats() · histogram(bucketMs) | 环形缓冲记录最近 N 帧耗时与长尾 | 2.1 |
FramePhases | { logic, submit, gpu, present } | 一帧四个阶段的耗时账本 | 2.1 |
frameTotalMs | frameTotalMs(phases) → number | 四段求和 | 2.1 |
withinBudget | withinBudget(phases, budgetMs = 1000/60) → boolean | 是否落在帧预算内 | 2.1 |
FixedStepLoop | new FixedStepLoop({ stepSeconds, maxStepsPerFrame }) · advance(dt, onStep?) → StepOutcome · reset() | 固定步长累加器;maxStepsPerFrame 是死亡螺旋的刹车 | 2.2 |
StepOutcome | { steps, alpha, droppedSeconds, accumulatorSeconds } | 一次 advance 的结果;alpha 恒在 [0, 1) | 2.2 |
interpolate | interpolate(prev, curr, alpha) → number | 在上一物理态与当前物理态之间补间 | 2.2 |
TimeScale | setScale(s) · scaled · unscaled | 时间缩放与未缩放两条时间轴 | 2.3 |
数学与数值
| 名称 | 签名 / 成员 | 说明 | 出处 |
|---|---|---|---|
Vec3 | add · sub · scale · dot · cross · length · normalize | 三维向量;遵循零分配约定(写进 out 参数) | 8.1 |
Mat4 | identity() · multiply(a, b) · transpose(m) · invert(m) · transformPoint(m, v) | 4×4 矩阵;列主序 | 8.2 |
Transform | compose(t, r, s) → Mat4 · invert(m) → Mat4 | 平移 / 旋转 / 缩放的复合顺序与逆变换 | 8.3 |
Euler | toQuat(e) → Quat · fromQuat(q) → Euler | 欧拉角与四元数互转 | 9.1 |
Quat | fromAxisAngle · mul · conjugate · normalize · slerp | 四元数运算与最短弧插值 | 9.1 / 9.2 |
bezier | bezier(p0, p1, p2, p3, t) → number | 三次贝塞尔曲线 | 9.3 |
catmullRom | catmullRom(p0, p1, p2, p3, t) → number | Catmull-Rom 样条 | 9.3 |
easeInOut | easeInOut(t) → number | 常用缓动函数 | 9.3 |
Fixed | Fixed.fromFloat(x) · .toFloat() · .mul(o) · .div(o) · .add(o) | Q16.16 定点数,为确定性模拟准备 | 10.2 |
approxEqual | approxEqual(a, b, eps) → boolean | 带阈值的浮点比较 | 10.3 |
saturate | saturate(x) → number | 把值夹到 [0, 1] | 10.3 |
safeDivide | safeDivide(a, b, fallback) → number | 除零防护 | 10.3 |
src/ecs/ 实体、组件与系统
这一层把 src/core/ 的句柄与稀疏集组装成 ECS。实体是句柄,组件存进稀疏集,系统声明读写依赖后被调度器并行分组。
| 名称 | 签名 / 成员 | 说明 | 出处 |
|---|---|---|---|
World | createEntity() → Entity · destroyEntity(e) · add<T>(e, c) · get<T>(e) · has<T>(e) · remove<T>(e) | ECS 世界;实体与组件的唯一入口 | 12.3 |
Entity | type Entity = Handle | 实体就是一个世代句柄 | 12.3 |
ComponentStorage<T> | add(e, c) · get(e) · remove(e) · view() | 单个组件类型的存储,底层是 SparseSet<T> | 12.2 |
ArchetypeStorage | archetypeOf(components) → Archetype · chunks() | 按组件集合分桶的存储方案 | 12.2 |
Query | new Query(world, [A, B]) · forEach(fn) · matching() → Entity[] | 组件集合求交;结果带缓存 | 13.1 |
QueryCache | invalidate(tag) · hit · miss | 查询结果缓存与失效 | 13.1 |
DirtyFlag | mark(e) · isDirty(e) · clear(e) | 变更检测与脏标记传播 | 13.2 |
SystemScheduler | addSystem({ reads, writes, run }) · groups() · tick(dt) | 声明读写依赖,推导并行分组 | 13.3 |
src/jobs/ 任务系统
第 14 章把计算搬出主线程,第 15 章把任务排成有依赖的图。整层都建立在 worker_threads(Node)或 Web Worker(浏览器)之上,通过 IWorkerPool 抽象掉差异。
| 名称 | 签名 / 成员 | 说明 | 出处 |
|---|---|---|---|
JobSystem | new JobSystem(workers) · submit(job) → Promise · waitAll() | 任务系统入口 | 14.1 |
IWorkerPool | spawn(task) · size · terminate() | 工作者池接口;Node / Web 双实现 | 14.2 |
NodeWorkerPool | new NodeWorkerPool(n) | 基于 worker_threads 的后端 | 14.2 |
WebWorkerPool | new WebWorkerPool(n) | 基于 Worker + SharedArrayBuffer 的后端 | 14.2 |
TaskQueue | push(t) · pop() · steal() | 任务队列与 Atomics 同步 | 14.2 |
WorkStealingQueue | push(t) · pop() · steal() | 每个工作者一个双端队列的窃取策略 | 14.3 |
DependencyDag | addNode(id) · addEdge(from, to) · topoOrder() | 依赖 DAG 的构建与拓扑执行 | 15.1 |
TaskGraph | addTask({ deps, run }) · build() · run() | 每帧构建任务图并调度到工作线程 | 15.2 |
src/reflect/ 反射与序列化
第 16 章用显式注册表(不用装饰器元数据)描述类型,第 17 章让序列化由这些类型信息驱动。生成器脚本在构建期扫描源码,把注册表写成一个普通 .ts 文件。
| 名称 | 签名 / 成员 | 说明 | 出处 |
|---|---|---|---|
TypeRegistry | register(type) · get(name) → TypeDescriptor · create(name) → unknown | 类型注册表 | 16.2 |
PropertyDescriptor | { name, type, offset, flags } | 属性描述符;编辑器与序列化的元数据 | 16.2 |
codegenScan | scan(srcDir) → registry.ts | 构建期扫描源码,生成类型注册表 | 16.3 |
Serializer | serialize(obj) → bytes · deserialize(bytes) → obj | 反射驱动的序列化接口 | 17.1 |
JsonSerializer | new JsonSerializer(registry) | 文本格式,便于调试 | 17.1 |
BinarySerializer | new BinarySerializer(registry) | 二进制格式,紧凑、快 | 17.1 |
migrate | migrate(save, fromVersion, toVersion) → save | schema 版本迁移 | 17.3 |
src/platform/ 平台抽象与基础设施
内核其余部分只依赖这里的接口,不直接 import node:fs 或 window。子系统编排、日志、配置也都收敛在这一层,作为启动期的公共设施注入。
| 名称 | 签名 / 成员 | 说明 | 出处 |
|---|---|---|---|
IPlatform | { name, time, file, thread, log } | 内核与宿主环境的唯一契约面 | 3.2 |
PlatformTime | nowSeconds() · nowMillis() | 单调时钟,不受系统时间改动影响 | 3.2 |
PlatformFile | readText(path) · writeText(path, data) · exists(path) | 同步文本读写,够启动期加载配置 | 3.2 |
PlatformThread | hardwareConcurrency · spawn(task) | 并发度与跨线程执行 | 3.2 |
PlatformLog | write(level, category, message) | 日志输出面 | 3.2 |
NodePlatform | NodePlatform: IPlatform | node:fs / perf_hooks / os 实现 | 3.2 |
WebPlatform | WebPlatform: IPlatform | 浏览器实现(fetch / Worker,L4 需手动验证) | 3.2 |
measureMillis | measureMillis(p, fn) → number | 平台无关的计时辅助,只吃 IPlatform | 3.2 |
ISubsystem | { name, dependencies, onInit, onStart, onStop, onShutdown } | 子系统四钩子接口 | 3.1 |
SubsystemRegistry | register(sub) · resolveOrder() · boot(ctx) · shutdown() · startOrder | 拓扑排序启动、逆序关闭、成环报错 | 3.1 |
Logger | debug/info/warn/error(category, msg) · setLevel(level) | 五级日志与分类开关 | 3.3 |
LogLevel | const LogLevel = { Debug: 0, Info: 1, Warn: 2, Error: 3 } as const | 日志级别(用 const 对象,不用 enum) | 3.3 |
CVarSystem | define(spec) · get(name) · set(name, value) · onChange(cb) | 类型化配置项 CVar | 3.3 |
assert | assert(cond, msg) · assertNever(x) | 断言语义与穷尽性检查 | 3.3 |
模块依赖方向
helio 只有一条铁律:依赖只能向下。任何「上层被下层 import」都是错误,1.1 节的 findUpwardDeps() 会在构建期把它抓出来。
platform ← 宿主边界:IPlatform / Logger / CVar / SubsystemRegistry
↑
core ← 内存、句柄、容器、数学、主循环(不依赖任何上层)
↑
ecs ← 依赖 core 的句柄与稀疏集
↑
jobs ← 依赖 core 的容器;ecs 借 jobs 并行跑系统
↑
reflect ← 依赖 core 与 ecs 暴露的类型信息
分层与依赖方向的完整讨论在1.1 引擎的分层与模块边界
;这里只需记住:core 永远不知道 ecs 的存在,platform 永远不知道上面有谁。
典型组合用法
查表不如看一次真实的串联。下面三段是全书最常见的组合,展示这些 API 怎么拼在一起。
创建世界、生成实体、挂组件、查询——ECS 的日常:
// snippet: ECS 的典型四步
const world = new World();
const e = world.createEntity();
world.add(e, { x: 0, y: 0, vx: 1, vy: 0 }); // Transform 组件
new Query(world, [Transform, Velocity]).forEach((entity, t, v) => {
t.x += v.x * dt;
t.y += v.y * dt;
});
用 arena 兜住一帧内的临时分配,帧末整体回收:
// snippet: 帧内临时内存走 ScratchArena
const scratch = new ScratchArena(64 * 1024);
const mark = scratch.mark();
const tmp = scratch.alloc(1024, 16); // 帧内临时数据
// ... 本帧用完即弃 ...
scratch.release(mark); // 偏移回到标记点,下一帧继续用
把一批互相独立的任务交给 JobSystem,最后等齐:
// snippet: 提交任务并等待
const jobs = new JobSystem(4);
const handles = entities.map((e) => jobs.submit(() => updateEntity(e, dt)));
await Promise.all(handles);
jobs.waitAll();
命名约定速记
- 句柄一律是 32 位
number,用NULL_HANDLE(0)表示「空」,绝不用-1或undefined混着来。 - 分配器一律「只前进不回收」;回收的语义是「标记为空闲、允许复用」(
reset/free/swap-remove),不是把内存还给系统。 - 时间一律从
Clock或IPlatform.time取,正文代码里不出现直接的performance.now()。 - 类型用
interface/type;级别枚举用const 对象 + as const,因为 Node 26 的 erasable syntax 禁止enum。 - 容器把「值」和「身份」分开:
SlotMap管身份(带世代),SparseSet管存储(dense 紧凑)。
按章反查
只想按章节找对应零件时,看这张表:
| 章节 | 关键类型 / API |
|---|---|
| 2.1 | Clock FakeClock FrameTimer FramePhases |
| 2.2 | FixedStepLoop StepOutcome interpolate |
| 2.3 | TimeScale |
| 3.1 | ISubsystem SubsystemRegistry |
| 3.2 | IPlatform NodePlatform measureMillis |
| 3.3 | Logger LogLevel CVarSystem assert |
| 4.2 | Arena ScratchArena |
| 4.3 | alignUp |
| 5.1 | ObjectPool PoolStats |
| 5.2 | BlockAllocator |
| 5.3 | MemoryTag |
| 6.2 | Handle makeHandle SlotMap NULL_HANDLE |
| 6.3 | SparseSet |
| 7.1 | DynamicArray OpenHashMap |
| 7.2 | BitSet RingBuffer IntrusiveList |
| 7.3 | StringIntern Guid |
| 8.x | Vec3 Mat4 Transform |
| 9.x | Euler Quat bezier catmullRom |
| 10.x | Fixed approxEqual saturate safeDivide |
| 12.x | World Entity ComponentStorage ArchetypeStorage |
| 13.x | Query DirtyFlag SystemScheduler |
| 14.x | JobSystem IWorkerPool TaskQueue WorkStealingQueue |
| 15.x | DependencyDag TaskGraph |
| 16.x | TypeRegistry PropertyDescriptor codegenScan |
| 17.x | Serializer JsonSerializer BinarySerializer migrate |
一处提醒:本表列的是接口与签名,不是实现。同一个名字在正文里可能有更细的字段与更严的边界检查(例如 SlotMap.remove 会先判 has、ObjectPool.release 会拦截重复归还),以正文的可编译代码为准。
阅读下一节:附录 B 环境与复现速查
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。