.gitignore添加.idea目录和.vscode目录
|
---
applyTo: "**/Benchmark/**"
---
# 性能测试指令
适用于性能测试、压力测试、基准测试、BenchmarkDotNet 相关任务。
---
## 1. 项目结构
- 基准测试统一放在 `Benchmark/` 项目,按主题分子目录(如 `PacketBenchmarks/`、`CacheBenchmarks/`)
- 入口 `Program.cs` 使用 `BenchmarkSwitcher` 模式,**不要修改**
- TFM 使用最新稳定版,`<LangVersion>latest</LangVersion>`
## 2. 代码规范
遵循主指令全部编码规范(类型名用 `String`/`Int32` 等、file-scoped namespace),另有以下补充:
- **命名空间**:`Benchmark.{主题}Benchmarks`
- **类名**:`{被测类型}Benchmark` 或 `{被测主题}Benchmark`
- **必须标注** `[MemoryDiagnoser]` 和 `[SimpleJob]`(需调整迭代次数时用 `[SimpleJob(iterationCount: N)]`)
- **方法描述**:`[Benchmark(Description = "中文描述")]`,方便报告阅读
- **参数化**:用 `[Params]` 或 `[ParamsSource]` 控制数据规模
- **初始化 / 清理**:分别放 `[GlobalSetup]` 和 `[GlobalCleanup]`
- **分组**:同类测试用 `#region` 分组
- **多线程并发**:动态线程数包含 CPU 核心数,推荐模板:
```csharp
public static IEnumerable<Int32> ThreadCounts
{
get
{
var cores = Environment.ProcessorCount;
var set = new SortedSet<Int32> { 1, 4, 8, 32 };
set.Add(cores);
return set;
}
}
[ParamsSource(nameof(ThreadCounts))]
public Int32 ThreadCount { get; set; }
```
## 3. 运行要求
- 必须以 **Release 模式**运行,获取有代表性的峰值数据
- 运行全部:`dotnet run -c Release`
- 运行指定类:`dotnet run -c Release -- --filter *ClassName*`
- ❌ 禁止在 Debug 模式下采集数据写入报告
## 4. 测试维度
- **并发维度**:单线程 + 多线程(多线程含与当前 CPU 核心数相同的并发数)
- **操作维度**:单一操作 + 批量操作
## 5. 常见错误
- ❌ 在 `[Benchmark]` 方法内做初始化(应放 `[GlobalSetup]`)
- ❌ 忽略返回值导致 JIT 死码消除(确保返回或赋值给字段)
- ❌ 手动 `Stopwatch` 计时(BDN 自动处理)
- ❌ `using` 的 `Dispose` 开销混入测量(仅在测试 Dispose 本身时才包含)
## 6. 报告存放
`Doc/Benchmark/{测试主题}性能测试.md`(UTF-8 无 BOM)
## 7. 报告结构(顺序固定)
1. **性能概览**(放最前:一句话点明被测功能用途 + 简单语言总结核心发现)
2. 测试环境 → 测试方法 → 测试结果(BDN 原始表格,保留 Mean/Error/StdDev/Allocated)
3. 核心指标(换算 msg/s、QPS 等业务指标)
4. **对比分析**
- 纵向:同场景不同并发趋势,找出最优并发点
- 横向:不同方案同并发差异百分比
5. 性能瓶颈定位(按重要程度排序)→ 优化建议(含预期收益与内存节省预估)
## 8. 性能瓶颈定位规范
性能瓶颈定位章节是报告的核心价值输出,必须遵循以下规范:
### 8.1 瓶颈点结构(每个瓶颈必须包含)
每个瓶颈点必须包含以下要素,缺一不可:
| 要素 | 说明 | 示例 |
|------|------|------|
| **优先级标签** | P0/P1/P2/P3,按影响程度降序 | P0 |
| **瓶颈名称** | 一句话准确描述瓶颈 | VisitTime 写入触发 MESI 缓存行争用 |
| **优化收益占比** | 该瓶颈在总体可优化空间中的占比 | ~35% |
| **现象与数据** | 用 BDN 实测数据量化问题严重程度 | 4T→8T 扩展仅 1.3x,低于预期 2.0x |
| **根因分析** | 从代码执行路径分析到底层硬件行为 | Get 每次写 VisitTime → 缓存行 Modified → 多核 MESI 失效 |
| **开销占比估算** | 在单次操作总耗时中的占比 | 占 Get 总耗时 30%~40% |
| **内存影响** | 每次操作的额外内存分配或 GC 压力 | 48 B/次装箱分配,32 线程累计 3 MB |
| **优化方向** | 具体可落地的优化方案 | 时间窗口内跳过更新(如 1s 内不重复写) |
| **预期收益** | 速度提升倍数 + 内存节省比例 | 多线程吞吐 +20-30%,消除缓存行争用 |
### 8.2 瓶颈分级标准
| 级别 | 定义 | 优化收益占比 | 行动 |
|------|------|------------|------|
| **P0** | 影响核心吞吐或造成 >30% 性能损失 | ≥25% | 必须优化 |
| **P1** | 影响多线程扩展性或造成显著内存压力 | 15%~25% | 建议优化 |
| **P2** | 特定场景下的次要瓶颈 | 5%~15% | 可选优化 |
| **P3** | 微小开销,仅在极端场景有影响 | <5% | 记录备查 |
### 8.3 瓶颈定位表格模板
性能瓶颈定位章节使用以下统一表格格式:
```markdown
### 核心瓶颈点总览
| 优先级 | 瓶颈 | 优化收益占比 | 当前开销 | 优化后预估 | 内存节省 |
|--------|------|------------|---------|-----------|---------|
| P0 | {瓶颈名称} | ~{X}% | {耗时/分配} | {目标值} | {节省比例} |
| P1 | {瓶颈名称} | ~{X}% | {耗时/分配} | {目标值} | {节省比例} |
| ... | ... | ... | ... | ... | ... |
```
### 8.4 内存优化方向表格模板
紧跟瓶颈总览表之后,补充内存优化方向:
```markdown
### 关键内存优化方向
| 优先级 | 优化方向 | 当前分配 | 优化后预估 | 节省比例 | 实施方案 |
|--------|---------|---------|-----------|---------|---------|
| P0 | {方向} | {X} B/op | {Y} B/op | {Z}% | {方案} |
| ... | ... | ... | ... | ... | ... |
```
### 8.5 开销拆解要求
对每个核心操作,必须给出开销来源拆解表:
```markdown
| 开销来源 | 占比估算 | 耗时估算 | 说明 |
|---------|---------|---------|------|
| {来源1} | ~{X}% | ~{N} ns | {原因} |
| {来源2} | ~{X}% | ~{N} ns | {原因} |
```
### 8.6 撰写原则
- **数据驱动**:所有结论必须有 BDN 实测数据支撑,禁止无数据臆测
- **量化优先**:用"快 X 倍"、"省 Y%"、"降 Z B/op"表达,避免"显著"、"明显"等模糊词
- **根因到底**:从应用层代码 → 运行时机制 → CPU 微架构逐层分析
- **可操作**:每个优化建议必须指明具体修改位置和实施方案,而非泛泛建议
- **排序严格**:P0 在前,P3 在后,同级按收益占比降序
|