Files
SystemSimulationApp/docs/other/C端结果编码与写出优化-2026-09-11.md
T
lujingze 3bc4be3c06 优化原生结果编码传输与浏览器缓存,记录八路性能基线
原生结果series通过字节索引直传,C端使用Ryu精确回读编码和64 KiB批量写出;网页采用Float64缓存和CSV工作线程,减少结果处理与保存等待。

补充八路AME曲线核查、全流程分阶段计时、独立编码基准和复现工具,固定后续优化采用修正八路及rtol=1e-8。C写出1.1808→0.1638 s,点击到可查看8.0100→6.9756 s。

验证:最终10项编码专项、29项相关后端回归通过;8份原生结果逐位一致,16次网页结果/CSV/刷新恢复通过。前端构建及缓存/CSV专项在本轮结果处理工作中通过。环境、原始大结果与临时构建不纳入Git。
2026-09-11 15:09:15 +00:00

19 KiB
Raw Blame History

C 端结果编码与写出优化(2026-09-11)

针对上一轮 八路全流程成本评估 中约1.18 s的C结果写出阶段,先比较编码和缓冲方案,再实现并验证真实网页路径。案例固定为 test-mql-8-corrected.json,未修改方程、局部管流求根、积分器、误差限、采样或前端生产代码。

已采用 Ryu精确回读编码 + 64 KiB批量写出。真实八路模型中,C编码写出墙钟中位数 1.1808 → 0.1638 s,减少86.13%;网页点击到结果可查看 8.0100 → 6.9756 s,减少12.91%,点击到浏览器缓存保存完成 8.1280 → 7.0841 s,减少12.84%。CSV下载保存没有观察到改善(1.1127 → 1.1506 s)。数值精度和求解路径保留,完整原生结果逐位一致,16次网页结果/CSV/刷新恢复核验通过。

上述网页收益是构建缓存命中的三次正式运行组中位数比较。首次编译另列:新依赖会增加冷编译成本,不能把缓存命中收益直接套用到首次运行。

调研与方案选择

原路径对约179万个结果数字逐值调用 fprintf("%s%.17g", …)。CPU时间与墙钟接近,只说明这一段主要在执行代码,仍需实验区分转换和写入成本。保留JSON合同可以继续使用已有Python原始片段传输、浏览器解析、缓存、CSV和结果文件流程。

候选比较基于作者源码与官方文档,未采用第三方性能宣传作为本项目的加速证据:

候选 本轮判断 一手依据
加大stdio缓存,保留 fprintf %.17g 改动小;必须实测是否能减少主要成本 当前 native/runtime/main.c 与下方重放实验
snprintf %.17g 到固定块,再 fwrite 仍使用相同浮点转换;可隔离stdio调用方式的收益 下方重放实验
Ryu binary64 shortest 采用;C接口、小型固定依赖、无分配转换,能精确回读原浮点值 固定版本源码、作者说明、边界测试
yyjson 的Schubfach路径 可用作后续对照,但完整 .c/.h 约756 kB,本轮不引入完整JSON库 0.13.0接口
独立yy_double / Dragonbox 前者属于作者基准仓库抽出版;后者官方实现要求C++11,本轮保留C11构建链 yy_double、Dragonbox

Ryu 的最短转换指足以恢复原始binary64的有效数字,不代表完整JSON字符数一定最少。其裸接口也会生成 NaN/Infinity;这些不是标准JSON数字,因此包装层必须明确拒绝,而非直接输出。相关规则见 Ryu源码、RFC 8259 §6。

本轮固定Ryu提交 4c0618b0e44f7ef027ebae05d2cc7812048f7c8f,原样保留相关C/头文件,选择Boost-1.0许可。源码及来源清单位于 native/encoding/ryu,头文件位于 native/include/ryu,许可同时随原生构建的 THIRD_PARTY_NOTICES.txt 分发。它是项目源代码依赖,不是本地运行环境;未安装新环境或新增Python/前端依赖。

独立编码与写入实验

将真实八路原生结果的全部series、final和finalState,共 1,790,486个binary64,在计时外转为预加载的连续double数组。每个候选预热一次、正式三次,顺序交替,先写真实文件并逐值按64位模式核对,再单独做 /dev/null 输出对照。

计时从 fopen 前到 fclose 后,包含编码、缓冲设置和写出;不含输入加载、输出投影、JSON静态键名准备或数值复核,也未调用 fsync。连续数据重放不包含生产代码的跨行矩阵读取,不能直接将微基准加速当作网页提速。

编码/写入候选 真实文件墙钟 / s 真实文件CPU / s /dev/null墙钟 / s 输出字节
原 fprintf %.17g 1.121005 1.120823 1.108461 32,725,281
fprintf + 1 MiB stdio缓存 1.121927 1.121777 1.107086 32,725,281
snprintf %.17g + 64 KiB批量 1.148625 1.148510 1.087661 32,725,281
裸Ryu + 64 KiB批量 0.100643 0.100637 0.071845 34,387,794

加大stdio缓存没有改善;保留相同浮点转换的 snprintf 批量方案反而稍慢。裸Ryu重放墙钟减少91.02%,且 /dev/null 中同样大幅加速,证据支持主要成本在浮点转换,而非仅文件写入等待。CPU/墙钟之差不是独立磁盘耗时。

裸Ryu总是采用科学计数法,重放文件反而增大约5.08%。因此生产包装层进一步比较普通与科学表示长度;下方生产C写出包含该重排和真实stride读取,不能与裸Ryu的0.1006 s混作同一测量。

原始依据:replay/summary.json,每次真实文件与计时记录均保留在 replay/file/。工具:benchmark_native_result_encoding.py。

实现

  • json_numbers.c 调用 d2s_buffered_n,用固定64 KiB栈缓冲批量写出数组,支持原输出矩阵的stride;每次调用返回前将自身缓冲交给FILE,保留调用者已有的stdio顺序和 ftell 边界。
  • Ryu输出后只进行十进制token重排:普通表示更短时采用普通表示,否则保留科学计数法。例如 1.2E1 → 12、1E-1 → 0.1;等长时不改。重排没有浮点运算或再次舍入,也不会展开巨大指数。负零固定输出 -0.0,普通Python JSON读取也能保留符号。
  • main.c 中 series/final/finalState 接入新编码。状态/整数计数/索引元数据、字符串转义、--probe/--init 的既有stdio路径保持原方式;不是所有C数字出口都改成Ryu。
  • 非有限结果数字、短写、ferror 或 fclose 失败都会阻止新结果索引发布并返回失败。数值数组未写完整时,不能把已写出的文件前缀视作成功结果。活动runner仍要求每次使用新的输出目录。
  • build.py 递归纳入嵌套头文件哈希,Ryu源码、查找表和许可记录都进入构建身份或随构建分发;不会误用优化前缓存。

JSON的字段、变量键名、列序、采样数和数值精度保留。数字拼写和文件SHA允许变化;不要求与旧 %.17g 的文本逐字相同。没有采用降低精度、减少采样、删列或有损压缩。

真实八路网页验证

输入SHA256为 670977bef67e62d9c66e8af497bada208bd72a7301be45128d185d47282cf288;157元件、178条连接、132状态、1784变量及时间列、1002个采样。0~10 s、0.01 s输出、CVODE BDF、rtol=1e-8、max_step=1e30及各状态atol下限保持原值。

旧版是本轮修改前从工作区冻结的代码(含此前结果传输/IDB/CSV优化),不是退回Git的旧后处理。旧版与新版各有无插桩组和阶段诊断组;每组预热一次、正式三次,串行运行。没有在正式计时期间安排其他大型构建、仿真或全量数值比对。前端全部使用相同生产资产,不改源码或构建。

用户端到端加速取同机无插桩两组;C写出细分取阶段诊断两组。页面“结果可查看”为成功完成DOM且按钮恢复可用,“保存完成”为IndexedDB事务提交后恢复指针发布的观察点。下载计时包括Playwright通知和 saveAs;本机回环HTTP不能代表远程网络。

三次正式运行的中位数(最小~最大),单位s。端到端来自无插桩组,C阶段来自独立诊断组;各组依次采集,未声称同序号是交替配对试验。降幅统一为两组中位数之比,小样本、系统调度与温度波动仍影响结果。

指标 优化前 优化后 用时变化
C编码写出(墙钟,诊断组) 1.1808(1.1529~1.2007) 0.1638(0.1625~0.1671) -86.13%
C编码写出(CPU,诊断组) 1.1804(1.1528~1.1991) 0.1638(0.1625~0.1671) -86.12%
点击运行 → 结果可查看 8.0100(7.9657~8.0463) 6.9756(6.9228~6.9863) -12.91%
点击运行 → 缓存保存完成(观察点) 8.1280(8.0499~8.1684) 7.0841(7.0053~7.0849) -12.84%
CSV点击 → 下载保存 1.1127(0.9959~1.3711) 1.1506(0.8617~1.3136) +3.41%
结果文件点击 → 下载保存 1.2254(1.1259~1.3546) 1.2662(0.9707~1.3405) +3.33%
积分求解(无插桩组) 6.0926(6.0571~6.0992) 6.0617(6.0598~6.0752) -0.51%

CSV和结果文件导出依然由已有浏览器路径生成,本轮未改。CSV用时波动范围重叠,不能判定加速;文件大小及SHA在全部16次运行间完全一致(31,820,845字节)。积分耗时的少量变化也不归因于编码:求解代码、计数和输出数值保持一致。

C写出与网页等待时间对比

生产 series 字节数 32,640,796 → 31,609,208(减少3.16%),诊断组HTTP响应体中位数 34,475,145 → 33,442,734字节(减少2.99%)。HTTP体另含元数据与进度,随时间文本略有波动;完整浏览器导出结果约33.85 MB,因浏览器重新编码而基本不变。

阶段诊断组的其他主要区间如下(单位ms,中位数):

阶段 优化前 优化后 边界说明
前端提交前预处理 18.800 22.600 模型检查、快照/XML生成及提交准备
后端XML校验 23.855 22.529 请求输入验证
网络编译 45.417 41.611 连接/方程编译
生成C 59.194 60.283 生成模型代码
构建缓存校验 45.505 34.094 正式运行全部命中
积分求解 6113.293 5957.849 含积分器、RHS/Jacobian、事件和采样
输出投影 85.804 79.819 重算采样点输出,位于编码前
C结果编码写出 1180.792 163.829 含fopen、编码、stdio、fclose及索引
Python索引结果读取 46.351 40.434 父区间,含字节读取/小元数据解析
HTTP结果事件组装编码 32.844 33.361 元数据JSON与原始series片段拼接
后端HTTP全程 7720.603 6553.295 包含上述后端子区间及ASGI发送等待
浏览器流文本解码 27.700 27.000 同步TextDecoder调用累计
浏览器结果JSON解析 99.700 104.300 单次原始JSON.parse

C写出占原生 main 时间的比例从 16.00%降至2.64%;新版积分占 96.05%,输出投影约 1.29%。这里先计算每次运行的阶段/父区间比例,再取中位数。浏览器解析未见改善;下一步更大的速度空间仍在求解计算,结果侧剩余CPU时间已明显缩小。

首次运行单列(无插桩组,各一次,构建缓存未命中):

观察值 / s 优化前 优化后
原生构建 3.6580 4.3720
点击到结果可查看 11.6815 11.3810
点击到缓存保存完成 11.8062 11.5025

本次新构建增加约0.714 s,抵消了大部分编码收益;首次页面运行仅缩短约0.30 s。这是单次冷构建观察,未做重复冷编译统计,不能推广为稳定冷启动提速。旧/新无插桩与诊断程序构建身份不同,均单独预热,不混入正式三次。

正确性、边界与限制

  • 数字编码最终 10项专项测试通过。覆盖34,254个有限binary64位模式、正负零、极大极小/次正规数、十进制边界与随机值;默认及 RYU_ONLY_64_BIT_OPS 两种路径均逐位回读一致。覆盖64 KiB边界、stride、非有限值拒绝、短写/零写、/dev/full 与关闭失败。见 最终测试日志。
  • 相关后端传输/取消、代码生成、HTTP、CSV与纯C后端 29项回归通过。该次同时运行当时9项数字编码测试,共38项;之后补充普通/科学token选择测试并重新运行最终10项编码测试。见 回归日志。
  • 8次诊断原生结果以一份旧版为基准,其余7份全部 1,790,486个数值逐位一致,每份含1,044个负零;series、final和finalState均覆盖,不使用容差或抽样。状态和求解计数也相同,仅排除求解墙钟/CPU元数据。见 native-bit-parity.json。
  • 16次真实网页完整series/final、CSV全部单元格、下载结果文件和刷新恢复通过;CSV比较 28,617,120个单元格,全部CSV SHA一致。网页比较是解析后的数值严格相等,原生64位检查另行补足负零验证。见 equality.json。
  • 原生CLI额外向 /dev/full 写出,返回退出码3且未发布结果索引。见 写失败验证。
  • 所有网页运行均为1002采样、1784变量,nfev=74,265、接受步6,974、拒绝步454、njev=475、nlu=1,656、事件1、启动4。输入、导入导出参数/连接与前端资产哈希一致。
  • 与本轮冻结源码比对,已有文件只改变C输出main、原生构建头文件扫描、README和许可;物理内核、积分器、代码生成方程、runner及前端生产资产未变。Ryu引入的C/头文件与Boost许可逐文件哈希等于固定上游快照。见 source-manifest.json。

本轮未改变数值求解算法,因此此前Amesim曲线差异结论保持不变。没有新的Amesim同工况CPU/墙钟数据,不作Amesim速度比较。源码兼容性考虑了GCC/MinGW,已在Linux GCC13.3实测默认及纯64位Ryu路径;本轮没有Windows运行实测。

后端输出期间仍包含输出投影;Python片段整理、传输、浏览器解析和保存也各有成本。父子区间及并行区间不能相加,独立阶段中位数不保证相加等于总耗时中位数。CSV生产实现本轮未修改,其下载用时差异仅记录为观察,不归因于C编码优化。

文件与复现

当前优化版无插桩网页保留在 http://127.0.0.1:8027/,可导入同一八路JSON复查。计时数据、下载、截图、临时构建和上游调研快照都在被Git忽略的 test/ 下;未安装新环境、提交或推送Git。源码Ryu依赖及许可应作为项目实现保留,不属于应忽略的本地运行环境。

仓库根目录运行。服务与浏览器应分两个终端启动,输出目录选新路径;下方只是新版复测例子,勿与其他仿真/编译并行。旧版重放使用 baseline-source/tests/manual/backend_stage_profile.py,显式 --frontend-dist frontend/dist;旧版诊断输出必须置于 baseline-source 内,以满足构建器的源码相对路径要求。

.venv/bin/python -m unittest tests.test_native_json_writer tests.test_native_result_transport tests.test_native_codegen tests.test_generic_system_xml_simulation tests.test_result_csv_export tests.test_native_only_backend -v

# 终端1:无插桩新版,新的端口和产物目录
.venv/bin/python tests/manual/backend_stage_profile.py --plain --port 8029 --output-dir test/c-encoding-recheck/backend

# 终端2:浏览器沿用已存在的本地运行条件
LD_LIBRARY_PATH="$PWD/.venv/native/browser-libs/usr/lib/x86_64-linux-gnu${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" .tools/node-v24.18.0-linux-x64/bin/node tests/manual/browser_stage_profile.mjs --url http://127.0.0.1:8029 --input tests/data/test-mql-8-corrected.json --mode control --runs 3 --output test/c-encoding-recheck/browser

诊断组去掉服务的 --plain,浏览器使用 --mode profiled;请另选新产物目录、端口并串行测试。独立编码实验、计时聚合和完整核对使用:

.venv/bin/python tests/manual/benchmark_native_result_encoding.py --result-json test/web-cost-20260911/native-compute-profile/control/run-1/result.json --output-dir test/c-encoding-recheck/replay --ryu-root test/c-result-encoding-20260911/ryu-upstream --run --warmups 1 --repeats 3 --dev-null
.venv/bin/python tests/manual/summarize_native_encoding.py --root test/c-result-encoding-20260911
.venv/bin/python tests/manual/compare_browser_stage_outputs.py test/c-result-encoding-20260911 --native test/web-cost-20260911/native-compute-profile/control/run-1/result.json --group baseline=test/c-result-encoding-20260911/browser-baseline --group baseline-profiled=test/c-result-encoding-20260911/browser-baseline-profiled --group optimized=test/c-result-encoding-20260911/browser-optimized --group optimized-profiled=test/c-result-encoding-20260911/browser-optimized-profiled --output test/c-result-encoding-20260911/equality.json

原生逐位工具 compare_native_result_bits.py 使用 --baseline 旧结果.json --candidate 新结果.json --output 报告.json,--candidate 可重复。准确输入路径记录于现有 native-bit-parity.json。图表由 plot_encoding.py 用系统Python/matplotlib生成,可缩放图为 overview.svg。大结果数值核对安排在全部性能计时结束之后。