4. 基准测试工具链选型与集成
后量子密码(PQC)算法性能评估的第一步,不是急着跑分,而是选对“尺子”。同一段 Kyber 或 Dilithium 代码,用不同工具链测出来的数字可能相差数倍,原因往往不在算法本身,而在测量层次、编译选项、计时粒度和运行环境。本节围绕四类主流工具链——liboqs、pqm4、SUPERCOP 以及自定义 harness——说明它们各自测什么、适合什么场景,以及如何接入到统一的评估流程中。
四类工具链的定位差异
理解选型的关键,是先分清“测什么”。liboqs 是 Open Quantum Safe 项目维护的 C 语言算法库,它把 KEM 和签名算法封装成统一 API,自带 speed、test_kem、test_sig 等基准程序,测量的是算法在通用 CPU 上的加解密/签名验签耗时与吞吐,适合做跨算法横向对比和集成验证。pqm4 面向 ARM Cortex-M4 平台,基于 STM32F4 Discovery 板,测量的是嵌入式设备上的周期数、栈占用和代码体积,是评估 PQC 在物联网端侧可行性的核心工具。SUPERCOP 是一套历史悠久的密码算法基准框架,强调可复现的编译与测量规范,通过统一的 crypto_* 接口和 measure 脚本输出周期计数,适合做算法实现的“标准答案”式对比。自定义 harness 则是针对特定评估目标自行编写的测量程序,灵活但需要自己保证测量正确性。
liboqs:通用 CPU 评估的首选入口
liboqs 的价值在于“统一”。它把 Kyber、Dilithium、Falcon、SPHINCS+ 等算法都包装成 OQS_KEM 和 OQS_SIG 结构体,调用方式一致,因此非常适合做算法间的横向对比。接入时通常先编译安装,再运行内置基准:
cmake -GNinja -DBUILD_SHARED_LIBS=ON -DOQS_BUILD_ONLY_LIB=OFF ..
ninja
./tests/speed_kem Kyber512
./tests/speed_sig Dilithium3
输出会给出 keygen、encaps、decaps(或 sign、verify)的平均耗时。需要注意两点:一是 liboqs 默认使用系统时钟计时,精度受限于 clock_gettime,对亚微秒级算法应增加迭代次数;二是它测的是“库封装后”的耗时,包含内存分配和函数调用开销,若关注纯算法周期数,应改用 SUPERCOP 或 pqm4。liboqs 还提供 OQS_PERFORMANCE 相关选项,可在编译期开启更细的统计。
pqm4:端侧周期数的权威来源
pqm4 的测量对象是 Cortex-M4 上的真实执行。它通过 DWT 周期计数器读取 DWT_CYCCNT,因此得到的是精确的 CPU 周期数,而非墙上时间。接入流程一般是:准备 STM32F4 Discovery 板,安装 ARM 工具链和 stlink,然后:
git clone https://github.com/mupq/pqm4.git
cd pqm4
make -j4 PLATFORM=stm32f4discovery
make flash
# 通过串口读取结果
python3 host.py /dev/ttyUSB0
pqm4 会输出每个算法的 keygen、encaps、decaps 周期数,以及栈使用峰值和代码段大小。这些数据对判断“该算法能否跑在 256KB Flash、64KB RAM 的 MCU 上”至关重要。例如 Kyber512 在 Cortex-M4 上 encaps 约几十万周期,若主频 168MHz,则单次封装约几毫秒,这个量级直接决定协议能否满足实时性。接入时务必固定主频和编译器版本,否则周期数会因优化差异而漂移。
SUPERCOP:可复现的“标准尺”
SUPERCOP 的设计哲学是“接口统一、测量规范”。每个算法实现放在 crypto_kem/ 或 crypto_sign/ 目录下,提供 crypto_kem_keypair、crypto_kem_enc、crypto_kem_dec 等固定函数。它的 measure 工具会多次运行并取周期计数,输出格式稳定,便于脚本化对比。接入 SUPERCOP 的典型做法是:把待测实现放入对应目录,运行 ./do 编译,再用 ./measure 采集。它的优势是“同一把尺子量所有实现”,适合在论文或评估报告中作为基准数据来源。缺点是配置较繁琐,且默认面向 x86 周期计数,迁移到其他架构需要调整计时后端。
自定义 harness:为特定问题定制测量
当评估目标超出上述工具链的能力时,就需要自定义 harness。典型场景包括:测量“批量验签”的吞吐、测量“密钥封装 + 网络传输”的端到端延迟、测量多线程下的并发性能、或测量特定内存对齐条件下的缓存行为。自定义 harness 的核心是保证计时正确:使用 rdtsc 或 clock_gettime(CLOCK_MONOTONIC),在测量前后插入内存屏障,多次采样取中位数,并排除首次运行的冷启动影响。一个最小示例:
#include <time.h>
double now() {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec + ts.tv_nsec * 1e-9;
}
// 测量 N 次取中位数
for (int i = 0; i < N; i++) {
double t0 = now();
crypto_kem_enc(ct, ss, pk);
double t1 = now();
samples[i] = t1 - t0;
}
自定义 harness 的灵活性最高,但也最容易出错。常见陷阱包括:编译器把测量代码优化掉、计时函数本身开销过大、未固定 CPU 频率导致动态调频干扰。建议在 harness 中加入 volatile 防止优化,并在测量前进行预热。
选型与集成的判断依据
四类工具链并非互斥,实际评估中常组合使用。若目标是“算法间横向对比”,优先 liboqs;若目标是“端侧可行性”,必须用 pqm4;若目标是“可复现的基准数据”,选 SUPERCOP;若目标是“特定场景的端到端指标”,用自定义 harness。接入统一评估流程时,应记录以下元数据:工具链名称与版本、编译选项(如 -O3 -march=native)、运行平台与主频、测量次数与统计方式(均值/中位数)、以及是否包含内存分配开销。只有这些信息齐全,不同来源的数据才具备可比性。
最后需要强调:工具链只是测量手段,评估结论仍取决于测量目标是否清晰。先明确“要回答什么问题”——是算法选型、端侧部署,还是协议集成——再决定用哪把尺子,才能让基准测试真正服务于工程决策。