专栏:HarmonyOS 三方库适配实战(28/30)
核心问题:怎样比较适配前后,而不制造"提升 50%"式伪证据?
摘要
性能数字脱离设备、构建模式、输入和样本量就没有可比性。本文为 ArkTS/HAR 与 Native 适配建立测量方案:固定候选产物、预热、输入集和统计口径,分别记录耗时、包体积、内存、错误率和稳定性。当前已完成 clean debug/release 的静态体积对照;无设备,不能给出运行耗时、内存或稳定性结论。
先写问题,再选指标
错误问题:"Native 比 ArkTS 快多少?"
可测问题:"在指定 arm64 设备、release 构建和 1 MiB 输入下,连续执行 200 次 XOR,端到端耗时的中位数与 P95 分别是多少,内存峰值和错误数是多少?"
只有后者能决定采样与工具。

图 1:环境、产物、输入、采样和统计口径必须同时固定。
最小测量记录
yaml
device: pending
system_api: pending
cpu_abi: arm64-v8a
build_mode: release
artifact_sha256: pending-release-candidate
scenario: xorBuffer end-to-end
input_sizes: [4096, 1048576, 16777216]
warmup: 20
samples: 200
statistics: [median, p95, max, error_count]
memory: peak_and_post_gc
当前已有离线 release 候选包,但没有设备运行数据。上面是待填写的采样配置,不是实测报告;开始采样前必须写入实际设备、系统和该次候选哈希,统计程序应拒绝 pending 字段。
原始样本与汇总结论要分开保存
报告不能只留下一个"平均耗时"。建议每次运行至少输出下面这些原始列:
csv
artifact_sha256,device,system_api,abi,build_mode,input_bytes,iteration,elapsed_us,result
<hash>,<device>,<api>,arm64-v8a,release,1048576,1,<measured>,success
汇总时再按同一设备、产物、模式和输入大小分组,计算样本数、中位数、P95、最大值和失败数。若丢失产物哈希或混入不同输入,数据不能直接合并。对比结果还应同时报告绝对差值与相对比例,避免基线很小时百分比看起来异常夸张。
一份可以支持结论的最小表应回答:比较了谁、在哪台设备、使用哪个候选包、运行多少次、失败多少次、波动多大。任一项为 pending,对应结论就继续标记为"未测"。
用同一统计脚本生成中位数与 P95
下面脚本只使用 Python 标准库,并采用 nearest-rank 定义计算 P95。它会把失败样本计入错误数,但不会把失败行的空耗时混入分布:
python
import csv
import math
import statistics
import sys
with open(sys.argv[1], newline="", encoding="utf-8") as f:
rows = list(csv.DictReader(f))
keys = ("artifact_sha256", "device", "system_api", "abi", "build_mode", "input_bytes")
groups = {}
for line, row in enumerate(rows, 2):
key = tuple(row.get(k, "").strip() for k in keys)
if not all(key) or any("pending" in v or "<" in v for v in key):
raise ValueError(f"line {line}: incomplete metadata")
if len(key[0]) != 64 or any(c not in "0123456789abcdefABCDEF" for c in key[0]):
raise ValueError(f"line {line}: invalid SHA-256")
if int(key[-1]) < 0:
raise ValueError(f"line {line}: negative input size")
group = groups.setdefault(key, {"ids": set(), "ok": [], "failed": 0})
iteration = int(row["iteration"])
if iteration < 1 or iteration in group["ids"]:
raise ValueError(f"line {line}: invalid/duplicate iteration")
group["ids"].add(iteration)
result = row["result"].strip()
if result == "success":
elapsed = float(row["elapsed_us"])
if not math.isfinite(elapsed) or elapsed < 0:
raise ValueError(f"line {line}: invalid elapsed_us")
group["ok"].append(elapsed)
elif result in ("error", "timeout", "cancelled", "crash"):
group["failed"] += 1
else:
raise ValueError(f"line {line}: unknown result")
if not groups:
raise ValueError("no samples")
for key, group in groups.items():
ordered = sorted(group["ok"])
print(dict(zip(keys, key), samples=len(group["ids"]),
success=len(ordered), failed=group["failed"],
median_us=statistics.median(ordered) if ordered else None,
p95_us=ordered[math.ceil(len(ordered) * .95) - 1] if ordered else None,
max_us=ordered[-1] if ordered else None))
保存为 summarize_perf.py,运行 python summarize_perf.py samples.csv。预热样本不写入此 CSV;每个文件只放一个场景和一次运行,同组 iteration 不能重复。比较多次独立运行时分别统计,不能把不同 run 的编号直接拼在一起。
对照与候选的设备、系统、构建模式、输入必须可比,产物哈希则各自固定,不要求两个不同实现具有同一个哈希。脚本按哈希分别分组,防止混入新候选。失败也计入样本数;全失败组的耗时为 None,不是 0 微秒。
可用人工构造数据检查统计逻辑:成功耗时 [1,2,3,4] 加一条 error,应有 samples=5、failed=1、median=2.5、nearest-rank P95=4。这个例子只验证算法,不能写成设备性能。混入另一输入大小必须产生另一组;负数、NaN、重复 iteration 应报错。小样本的 P95 往往等于最大值,所以不能用四个样本推导稳定尾延迟。
结论建议写成"在上述单一设备与输入下,候选的中位数变化为 X 微秒,P95 变化为 Y 微秒",而不是外推成"所有 HarmonyOS 设备提升 Z%"。本文没有采样,所以这里只给出计算方法,不填数字。
当前能够复现的离线体积基线
运行性能尚未测量,但候选文件大小和哈希可以离线读回。本轮 debug 产物为:
| 产物 | 字节数 | SHA-256 |
|---|---|---|
library_adapter.har |
3,003 | 50662C78...EF05B900 |
entry-default-unsigned.hap |
2,835,503 | D8E2638E...481DC688 |
arm64-v8a libtextnative.so(strip 后) |
78,496 | 7061FD42...150FFC7 |
x86_64 libtextnative.so(strip 后) |
78,976 | D410C2AC...D04F0B8 |
这些数字只描述当前 debug 候选,不能与不存在的"适配前 HAP"计算增量,也不能当作下载或安装体积。复查命令:
powershell
Get-Item .\library_adapter.har,
.\entry-default-unsigned.hap |
Select-Object Name, Length
Get-FileHash .\library_adapter.har -Algorithm SHA256
Get-FileHash .\entry-default-unsigned.hap -Algorithm SHA256
这些值来自 2026-10-19 在加入 ArrayBuffer 完整边界、NativeCounter.close() 和 normalizeAsciiAsync() 后,清理 Hvigor 与两个 debug ABI Native 目标再重建的候选。下一次比较必须固定构建模式、ABI 集合和压缩口径;任一哈希改变,就把它视为新候选重新采样。
下表是本次文章深审之前保存的同源码、同 ABI 集合候选,后续对象/异步桥接修订产生的新 HAP 不与本表混用。这里只比较两次候选的静态尺寸,不能完全归因为优化级别,也不能解释成下载体积或运行性能:
| 产物 | clean debug | release | release - debug |
|---|---|---|---|
| HAR | 3,003 B | 3,009 B | +6 B |
| HAP | 2,835,503 B | 2,787,192 B | -48,311 B |
arm64-v8a .so(strip 后) |
78,496 B | 57,600 B | -20,896 B |
x86_64 .so(strip 后) |
78,976 B | 58,768 B | -20,208 B |
release 使用 -O2 -DNDEBUG,clean debug 使用 -O0 -g;HAP 又受压缩与归档元数据影响,所以这里的差值只描述本轮两个候选。切换模式时若只执行 hvigor clean,entry/.cxx 可能继续复用旧 Native 输出;本轮通过 compile_commands.json 和 .so 尺寸发现污染,并对两个 debug ABI 执行 ninja clean 后重建。这个检查比只相信 -p buildMode=debug 更可靠。
同源码重建时,两个 .so 的 SHA-256 保持稳定,HAR/HAP 哈希变化而文件清单不变。这个现象尚不足以归因于元数据:名称相同的内部文件也可能改变。应先比较全部成员的内容哈希,再查归档时间戳和压缩参数。哈希可标识候选,但不同哈希不能直接归因于业务代码。
端到端与核心耗时分开
Native 接口至少包含:
text
ArkTS input preparation
+ bridge validation
+ input copy/view setup
+ C++ algorithm
+ output creation/copy
+ ArkTS consumption
只测 C++ 循环会忽略桥接和复制;只测一次端到端又无法定位瓶颈。两者都应测,并使用相同输入。
包体积怎样记录
至少比较:
- 基线 HAP/HAR 大小;
- 加入库后的 HAP/HAR 大小;
- 每个 ABI 的
.so原始/strip 后大小; libc++_shared.so等新增运行库;- debug 与 release 差异;
- 压缩包大小与安装后大小口径。
文件大小是可离线读取的事实,但不能直接等同于下载体积或安装占用。
稳定性不等于"没有崩"
稳定性报告应包含:
- 运行次数和总时长;
- 成功、业务错误、超时、取消与崩溃数量;
- 页面切换、多实例和后台/前台场景;
- 内存是否回落;
- FaultLog/ASan/Freeze 是否出现;
- 失败输入能否复现。
样本量为零时,错误率不是 0%,而是"未测"。

图 2:耗时、体积、内存、错误率与稳定性分开记录,避免单指标包装。
使用工具而不是手工秒表
官方测试服务提供白盒性能测试、Hypium、自定义性能测试、AppAnalyzer、Profiler 和云测试入口。场景检测应保持设备解锁、测试完整结束,并保存报告与调优文件。工具本身不会替你决定样本量和对照组。
当前可报告的事实
本地能确认双 ABI .so 已进入 HAP,clean debug/release 均有保存的候选、尺寸与 SHA-256。由于没有设备、签名/混淆 release 和采样,不能报告:
- ArkTS 与 Native 谁更快;
- 平均/中位/P95 耗时;
- 内存峰值;
- 错误率或稳定运行时长;
- AppAnalyzer 或 Profiler 无告警。
常见失败:只跑一次并计算百分比
单次结果受到 JIT/AOT 状态、缓存、调度和温度影响。百分比还会放大很小的绝对差异。必须给出原始口径、分布和可重复步骤;没有显著差异时诚实报告"当前样本无法支持结论"。
本篇产物与阶段门槛
本篇产物是一份可填写的质量报告模板,以及一次 clean debug/release 静态体积对照和缓存污染复现。第五阶段的运行性能和稳定性门槛因无设备尚未完成。
官方来源
- HarmonyOS 开发者测试服务入门:用于白盒性能测试、Hypium、AppAnalyzer、云测试与 APMS 入口。访问日期:2026-10-18。
- AppAnalyzer 场景化体检:用于设备、场景、报告和 Profiler 调优文件的流程。访问日期:2026-10-18。
- HarmonyOS 故障定位分析:用于崩溃、泄漏、冻屏和性能证据链。访问日期:2026-10-18。
验证声明
本文完成测量方案与官方工具核对,实际构建并读回 clean debug/release 产物、Native 编译参数、尺寸与哈希。没有设备、签名/混淆 release、Profiler、AppAnalyzer 或性能采样;不提供任何运行性能、内存或稳定性数字。