| 原则 | 说明 | 我的建议 |
|---|---|---|
| 可重复性 | 同样的代码、同样的环境,跑出来结果应该基本一致 | 至少跑5次取中位数 |
| 隔离性 | 被测代码不要受其他进程干扰 | 关掉后台程序、设置CPU亲和性 |
| 代表性 | 测试场景要贴近真实使用 | 别只测最理想的情况 |
| 可比较性 | 不同版本之间能直接对比 | 保持测试条件一致 |
// 错误示范
auto start = high_resolution_clock::now();
for (int i = 0; i < 100000; ++i) {
result = result + "a";
}
auto end = high_resolution_clock::now();
能发现问题吗?循环里每次都要创建新字符串,测的是字符串拷贝的性能,根本不是拼接函数的性能。这就是典型的“测试代码污染了测试结果”。
## 常见基准测试框架
### Google Benchmark
这个框架我用了好几年。它最方便的地方是——**你只需要关注被测代码,调度、统计、报告这些脏活它全包了**。
先看个例子:
#include <benchmark/benchmark.h>
static void BM_StringCreation(benchmark::State& state) {
for (auto _ : state) {
std::string empty_string;
}
}
BENCHMARK(BM_StringCreation);
static void BM_StringCopy(benchmark::State& state) {
std::string x = "hello";
for (auto _ : state) {
std::string copy(x);
}
}
BENCHMARK(BM_StringCopy);
BENCHMARK_MAIN();
这段代码测的是空字符串创建和字符串拷贝的开销。Google Benchmark会自动:
- 计算每次迭代的时间
- 排除循环本身的开销
- 输出统计信息(均值、方差等)
**常用功能**
| 功能 | 用法 | 说明 |
|---|---|---|
| 参数化 | BENCHMARK(BM_test)->Arg(8)->Arg(64) |
测试不同参数组合 |
| 多线程 | BENCHMARK(BM_test)->Threads(4) |
模拟多线程并发 |
| 自定义标签 | state.SetLabel("label") |
给测试结果加说明 |
| 跳过迭代 | state.SkipWithError("reason") |
条件不满足时跳过测试 |
我的习惯:对于有外部依赖的测试,使用
### C++ Benchmark的其他选择
除了Google Benchmark,还有几个值得关注:
| 框架 | 特点 | 适用场景 |
| --- | --- | --- |
| **Nonius** | 轻量、单头文件 | 快速原型测试 |
| **Celero** | 支持图像化输出 | 需要可视化报告 |
| **hayai** | 专注微基准测试 | 毫秒级以下耗时 |
不过说实话,现在Google Benchmark基本是事实标准。除非你项目实在用不了CMake,不然我建议直接上Google Benchmark。
### 其他语言场景
SDK开发不只有C++。我在iOS和Android上也做过不少基准测试:
**iOS (Swift)**
Iterations(100)手动控制迭代次数,然后再做统计分析。
import XCTest
class SDKBechmarks: XCTestCase {
func testEncodingPerformance() {
measure {
// 被测代码
encode(data)
}
}
}
`XCTest`的`measure`方法会自动采集耗时,并且支持基线对比。你可以在Xcode里设置“性能测试基线”,一旦新版本慢于基线,测试就会失败。这个功能在CI/CD里特别好用。
**Android (Java/Kotlin)**
@RunWith(AndroidJUnit4::class)
class SdkBenchmark {
@get:Rule
val benchmarkRule = BenchmarkRule()
@Test
fun testDecode() {
benchmarkRule.measureRepeated {
runWithTiming { decoder.decode(data) }
}
}
}
Android的`BenchmarkRule`会自动处理预热、隔离、统计。不过要注意的是,这些测试要在真机上跑。模拟器的CPU/内存模型和真机差距很大,测出来的数据基本没用。
### 基准测试的核心流程
核心流程:明确假设 → 选择测试用例 → 准备测试环境 → 编写基准代码 → 执行测试 → 分析结果 → 报告与决策
警告:基准测试的结果永远是“当时当地”的。换了CPU、换了操作系统、甚至只是后台进程变了,结果都可能不一样。不要迷信单次测试的数字,要看趋势。
## 知识体系:基准测试框架结构
## 实践建议
### 如何选择框架?
1. **如果你写C++**:无脑上Google Benchmark。社区活跃,工具链成熟,文档齐全。
2. **如果你做iOS开发**:用XCTest自带的`measure`就够了。不够的话,考虑Swift Benchmark Suite。
3. **如果你做Android开发**:用`BenchmarkRule`。记得在真机上跑。
### 什么时候不该用基准测试?
这个问题我问过自己很多次。其实只有一个答案:
**当你的代码还在频繁改逻辑的时候,别急着做基准测试。**
基准测试的投入产出比,取决于被测代码的稳定性。你改一行逻辑,所有数据都要重跑。那还不如先把功能调稳了,再开始做性能基线。