# 压测方法与报告 [English](./bench.md) | 简体中文 本文件记录压测的方法学与实测数据,使他人能够复现并判断结论是否可信。 ## 方法学 ### 三种测量,分别回答不同问题 | 工具 | 回答的问题 | | --- | --- | | `bench` | 单次调用有多快 | | `bench_scale` | 成本随规模如何增长 | | `bench_parallel` | 多个核心能省多少 | 三者都在 `apsis-rs` 门面中,以 example 形式提供,release 模式运行: ```bash cargo run --release -p apsis-rs --features full --example bench cargo run --release -p apsis-rs --features full --example bench_scale cargo run --release -p apsis-rs --features full --example bench_parallel ``` ### 三条原则 **无预热。** 每项测量前不丢弃任何一次调用。首次调用会付出惰性分配与缓存的代价, 而调用方等待的正是这个代价,把它藏在一次被丢弃的运行里,等于报告了一个没有人 真正等待过的数字。因此 `bench_scale` 同时给出**冷启动**与**稳态**两列。 **取多次最优。** 串行基线跑若干次取最快。调度抖动只会让某一次变慢,不会让所有 次都变慢;取最快得到的是干净的信号,而不是被邻居进程污染的下界。`bench_parallel` 用这一方式。 **报告每单位成本。** 绝对值随机器变化,`ns per unit` 不变。看后者才能判断复杂度 是否正确。 ### 复杂度判据 对输入规模 n,`ns per unit` 恒定即 O(1) 每单位(线性总时间);`ns per unit` 按 n 的幂增长即该幂次的总复杂度。指数由相邻两点的比值取对数得到。 ## 测量环境 | 项 | 值 | | --- | --- | | CPU | AMD Ryzen 7 8845HS(8 核 16 线程) | | 内存 | 28 GB | | 工具链 | rustc 1.98.1,release | | 依赖 | 无第三方,基准为 example | 绝对值只在本机有意义。跨机器比较应看同一工具的**比值**。 ## 结果一:单次调用 | 路径 | 每次调用 | | --- | --- | | Hermite 星历插值查询 | 24.3 ns | | TEME 到地固系旋转 | 26.8 ns | | 开普勒根数转状态 | 38.1 ns | | 地固系转大地坐标 | 50.1 ns | | 链路预算 C/N0 | 60.6 ns | | SGP4 传播(历元后 30 分) | 248.7 ns | | 可见性搜索(120 样本) | 7.49 μs | | 规划 60 个任务(1 资源) | 239 μs | 星历查询比它所替代的 SGP4 传播快约十倍,这正是 `apsis-ephemeris` 存在的理由: 一次建表、多次查询时,插值远便宜于传播。 ## 结果二:规模扫描 ### 星座一日传播(60 秒步长,1441 样本/星) | 星数 | 冷启动 | 稳态 | 每样本 | | --- | --- | --- | --- | | 24 | 8.7 ms | 8.7 ms | 252 ns | | 240 | 80.5 ms | 80.0 ms | 231 ns | | 1008 | 339.5 ms | 339.5 ms | 234 ns | | **2016** | 679.4 ms | 679.4 ms | 234 ns | **每样本成本恒定(增长指数 0.00)**,即 SGP4 单次传播是 O(1),总时间线性于星数。 两千余星的一日覆盖约 0.7 秒。冷启动与稳态几乎相同,说明该路径没有显著的 一次性分配。 ### 可见性搜索(单站,每轨 2 小时) | 轨数 | 每星 | | --- | --- | | 8 | 7.3 μs | | 128 | 7.3 μs | **恒定(增长指数 0.00)**,每条轨道独立,线性于星数。 ### 拓扑构建与路由(4500 km 链路) | 星数 | 每星 | 增长指数 | | --- | --- | --- | | 8 | 237 ns | — | | 64 | 2.2 μs | 1.07 | | 120 | 5.7 μs | 1.52 | | 300 | 33.0 μs | 1.93 | | **600** | **182.6 μs** | 2.47 | **约 O(n²)**。每星成本随星数增长,指数从 1 升到 2.5。成因是几何判定本身 就是全对检查:n 颗星有 n² 对要判断。并行扫描证实瓶颈在此。 ### 任务规划(单资源,50 长优先级链) | 任务数 | 每任务 | 增长指数 | | --- | --- | --- | | 60 | 4.5 μs | — | | 200 | 16.0 μs | 1.05 | | 1000 | 108.4 μs | 1.33 | | **2000** | **314.1 μs** | 1.54 | **约 O(n²),大 n 时趋向 O(n)**。贪心放置本质上按时间序逐个安排,每次放置 都要与已放置的 placement 比较,故二次是算法本身而非实现缺陷。 ## 结果三:并行扫描 ### 传播(按卫星分块) | 星数 | 1 线程 | 2 | 4 | 8 | 16 | 最佳加速 | | --- | --- | --- | --- | --- | --- | --- | | 550 | 184 ms | 1.96× | 3.83× | 6.86× | 8.12× | 8.12× (16) | | 1000 | 336 ms | 1.89× | 3.73× | 6.49× | 8.24× | 8.24× (16) | | 2000 | 671 ms | 1.89× | 3.74× | 6.61× | 7.93× | 7.93× (16) | **传播是 embarrassingly parallel**:每个卫星独立,2 至 8 线程接近线性。 16 线程效率降到约 0.5,即超线程带来争用。**推荐 8 线程**(效率约 0.85), 或 16 线程换取更高绝对吞吐。 ### 可见性搜索 | 轨数 | 1 线程 | 4 | 8 | 16 | 最佳加速 | | --- | --- | --- | --- | --- | --- | | 64 | 0.47 ms | 2.74× | 2.68× | 1.87× | 2.74× (4) | | 256 | 1.95 ms | 3.52× | 4.36× | 5.27× | 5.27× (16) | **小规模加速有限**,因为线程启动的固定开销在小工作量上占主导。256 轨约 5.3×。 ### 拓扑构建(阶段拆分) | 星数 | 可见性(可并行) | 边折叠(串行尾部) | 最佳加速 | | --- | --- | --- | --- | | 120 | 0.31 ms | 0.30 ms | 1.37× | | 300 | 1.93 ms | 3.84 ms | 1.72× | **该路径并行收益有限**。300 星时串行的边折叠占 3.84 ms,是可并行部分的两倍, 故总时间最多加速约 1.7 倍,远不及数值内核的接近线性。根因是 `add_edge` 查重时 线性扫描已有边,使插入本身成为 O(n³)。 ## 结论 | 路径 | 复杂度 | 并行性 | 实用规模表现 | | --- | --- | --- | --- | | SGP4 传播 | O(1)/样本 | 优秀,16 线程 ~8× | 2016 星一日 0.68 s;16 线程 85 ms | | 星历插值查询 | O(log n) | 随传播 | 24 ns/次 | | 可见性搜索 | O(1)/星 | 良好,16 线程 ~5× | 256 轨 0.37 ms | | 坐标与链路 | O(1) | 不必并行 | 27 至 61 ns | | 任务规划 | ~O(n²) | 串行 | 2000 任务 0.63 s | | 拓扑构建 | ~O(n²) | 收益有限(串行尾部主导) | 600 星 110 ms | **要点** 1. **数值内核是线性且高度可并行的。** 两千余星的一日轨道预测单线程 0.68 秒, 16 线程 85 毫秒。 2. **星历插值比传播快十倍**,密集查询场景应先用传播建表再插值。 3. **两个组合算法受限于算法而非并行度**:任务规划是贪心的固有二次,拓扑构建的 边插入查重是二次的平方,后者并行化最多只能加速约 1.7 倍。欲再提升需改算法。 4. **传播推荐 8 线程**:并行效率约 0.85,16 线程因超线程争用降至约 0.5。 ## 复现 ```bash # 全部门禁,确保测量的是正确代码 cargo test --workspace --all-features # 三种测量 cargo run --release -p apsis-rs --features full --example bench cargo run --release -p apsis-rs --features full --example bench_scale cargo run --release -p apsis-rs --features full --example bench_parallel ``` 测量之间不要并发运行其它负载。绝对值随机器变化,结论应基于同一工具内的比值。