-
Notifications
You must be signed in to change notification settings - Fork 0
Performance and Benchmarks
运行可复现的基准测试、对比记录的基线,并优化已测量的热路径。
适用于 4.0.0。
统一基准测试项目位于 benchmark/,覆盖运行时热路径和编译器流水线。
# 快速冒烟检查
dotnet run --project benchmark/Benchmark.csproj -c Release -- --smoke
# 写入简单 CSV 对比
dotnet run --project benchmark/Benchmark.csproj -c Release -- --compare
# 运行完整 BenchmarkDotNet 套件
dotnet run --project benchmark/Benchmark.csproj -c Release使用上述命令运行冒烟检查、简单 CSV 对比或完整 BenchmarkDotNet 套件。
覆盖范围包括 domain 创建、空调用、函数调用、模块调用、闭包调用、对象、数组、HashMap、字符串、JSON、TDoc、Regex、CLR 互操作,以及词法分析器、解析器、发射器、单/多模块编译和 CompileBlock。
TDoc 基准测试覆盖原始类型读写、普通嵌套对象读写、通用 Array、Int32Array 和宽对象读取,以跟踪解析器热路径、普通对象写入和 packed array 分配回归。
dotnet run --project benchmark/Benchmark.csproj -c Release -- --filter "*TypedDocument*"以下结果来自 2026-08-20 的 Release/net10.0 BenchmarkDotNet ShortRun:Windows 11、Intel Xeon W-2235、.NET SDK 10.0.400 和 Runtime .NET 10.0.11。ShortRun 用于回归对比,而非跨机器的绝对 SLA 保证。
| Benchmark | Mean | Allocated | 说明 |
|---|---|---|---|
SerializePrimitive |
226.0 ns | 32 B | 结果字符串分配;无中间树 |
DeserializePrimitive |
246.7 ns | 0 B | 流式扫描与直接绑定 |
SerializeNestedObject |
1.289 µs | 296 B | 规范文本与结果字符串 |
DeserializeNestedObject |
2.410 µs | 640 B | 构造目标脚本对象 |
SerializePackedInt32Array1024 |
144.817 µs | 20,360 B | 文本输出是主要分配 |
DeserializePackedInt32Array1024 |
168.155 µs | 4,192 B | 目标 int[]/packed array |
DeserializeGenericArray1024 |
178.815 µs | 32,992 B | 每元素通用 ScriptDatum 存储 |
DeserializeWideObject256 |
371.489 µs | 16,608 B | 目标对象上 256 个属性 |
该表记录平均耗时和管理内存分配。原始类型解析保持零分配;此处显示的对象和数组分配是返回的数据或输出文本,而非中间语法树。
例如,对于约 1 MB 的 map.tdoc,报告如 AuroraTypedDocument.Deserialize | 38 ms | 1,000,136 KB 应视为端到端分配和吞吐量信号,而非仅解析器成本。主要成本通常是返回的对象图(每个字段一条属性项和值包装)、键/值字符串创建、字典增长、数值转换和垃圾回收工作。流式读取器避免中间语法树,但无法避免分配调用方要求物化的值。
要减少大型 map 的耗时和分配:
- 保持读取器流式,并直接绑定到最终
ScriptObject/HashMap;不要先解析到临时通用树。 - 字段数已知时预分配 map 大小,并在 API 允许时复用 serializer/reader 实例和临时缓冲区。
- 对同构批量数据使用 packed array(
Int32Array、UInt8Array、Float64Array及其他 typed array),而非通用ScriptDatum值数组。typeof报告构造函数名;datumKind保持Object。 - 避免重复子字符串/键复制;仅在宿主控制生命周期时对稳定 schema 键进行 intern 或池化。
- 用原始类型、嵌套对象、宽对象、通用数组和 packed array 案例的基准测试分离解析器成本与物化成本。预热后报告 mean、p95 和管理内存分配。
具体收益取决于 map 的字段数、嵌套、值种类以及目标对象是否可复用。不要用单次 Stopwatch 运行跨机器对比;使用基准测试项目并保持输入和运行时固定。
编译器将稳定的数值局部变量、Boolean 条件、整数循环变量和已知 packed array 引用保存在原生 CIL 表示中。详见 类型与强类型。模块边界热函数见 Native 函数。
以下结果来自 2026-08-15 的 Release/net10.0 BenchmarkDotNet ShortRun。它们是文档基线;不带 --filter 运行以重新生成完整套件。
dotnet run --project benchmark/Benchmark.csproj -c Release -- \
--filter "*FunctionCallLoop*" "*ModuleCallLoop*" "*NumericLoop*"| Benchmark | Size | Mean | Allocated | 观察 |
|---|---|---|---|---|
NumericLoop |
1,000 | 1.477 µs | 0 B | 数值局部变量与算术保持原生 CIL |
NumericLoop |
10,000 | 13.483 µs | 0 B | 线性时间增长,循环无分配 |
FunctionCallLoop |
1,000 | 82.668 µs | 0 B | 同模块数值调用使用原生特化 |
FunctionCallLoop |
10,000 | 766.803 µs | 0 B | 复用调用帧;无每调用上下文分配 |
ModuleCallLoop |
1,000 | 122.591 µs | 0 B | 零分配跨模块通用调用边界 |
ModuleCallLoop |
10,000 | 1.186 ms | 0 B | 受控动态分派成本,无每调用分配 |
该表记录文档热路径的 mean 和分配。六个案例在执行路径上均测得零管理内存分配。
环境为 BenchmarkDotNet 0.15.8、Windows 11 10.0.28000.2704、Intel Core i7-13700KF、.NET SDK 10.0.400、Runtime .NET 10.0.11 和 ShortRun job。基准测试复用预构建的宿主参数数组,因此 Allocated 仅反映实际执行路径。
- 在热循环内缓存
length、模块常量和重复属性读取。 - 对已知长度的数值工作使用 packed array 而非通用
Array。 - 对稳定 shape 使用
export type。 - 对模块边界热辅助函数使用
native func。 - 对大量文本构造使用
StringBuffer,而非循环中重复字符串拼接。 - 仅在确实需要持久模块输出或调试符号时选择
Persistence;否则使用满足需求的最轻模式。 - 优化前先测量,之后同时运行功能测试和基准测试。
AuroraScript.JIT 4.0.0 · 文档首页 · 仓库 · MIT License