Skip to content
Gains Summary
Main Navigation 首页 / Home
C++ 编程 / C++ Programming
系统与高性能 / Systems & Performance
Web 开发 / Web Development
人工智能 / Artificial Intelligence
工业软件 / Industrial Software
其他内容 / Other Topics
C++ 编程 / C++系统与性能 / SystemsWeb 开发 / Web人工智能 / AI工业软件 / Industrial

外观

Sidebar Navigation

← 工业软件 / Industrial Software

求解器工程 / Solver Engineering

1. 工业求解器工程:从物理方程到体系结构优化 / Industrial Solver Engineering from Physical Equations to Architecture Optimization

2. 生产级求解器架构:模块边界、执行管线与可观测性 / Production Solver Architecture with Module Boundaries, Pipelines, and Observability

3. 结构求解器设计:从单元积分到非线性、动力与接触 / Structural Solver Design from Element Integration to Contact and Dynamics

4. CFD 求解器设计:有限体积、压力速度耦合与并行时间推进 / CFD Solver Design with Finite Volumes and Pressure-Velocity Coupling

5. 求解器数据结构:稀疏矩阵、场布局、Cache、NUMA 与 Ghost 数据 / Solver Data Structures for Sparse Matrices, Fields, NUMA, and Ghost Data

6. CAE 性能分析:CPU、GPU、NUMA、MPI 与 I/O 工具链 / CAE Performance Analysis Across CPUs, GPUs, NUMA, MPI, and I/O

7. 体系结构驱动优化:从性能证据到算法、数据与并行处理 / Architecture-Driven Optimization from Evidence to Algorithms and Parallelism

8. 求解器优化案例:结构算子、CFD 通量与 MPI 强扩展 / Solver Optimization Cases for Structural Kernels, CFD Fluxes, and MPI Scaling

本页目录

求解器优化案例:结构算子、CFD 通量与 MPI 强扩展 / Solver Optimization Cases for Structural Kernels, CFD Fluxes, and MPI Scaling ​

📅 创建时间:2026-07-23
🏷️ 标签:#性能案例 #SpMV #CFD通量 #MPI扩展 #Benchmark
📚 前置知识:[[06-architecture-driven-optimization]]
📚 相关知识:[[/06-other/01-projects/profile/C++/2-StructKernelBench/2-StructKernelBench]] [[../marine-cae/15-verification-and-validation]]


1. 案例的统一写法 ​

一个可信的优化案例必须包含:

text
场景和工程目标
输入规模和硬件环境
正确性标准
端到端基线
阶段和热点证据
可证伪假设
唯一主要修改
优化后测量
迭代次数和工程结果检查
结论与下一实验
1
2
3
4
5
6
7
8
9
10

下面数值用于演示分析方法,不代表某个商业软件的实测结果。实际项目必须保存原始日志和命令。

2. 案例一:结构求解中的 CSR SpMV ​

2.1 场景 ​

三维线弹性模型:

  • 500 万自由度;
  • 每个节点 3 个位移自由度;
  • CG + AMG;
  • 线性求解占端到端时间 76%;
  • SpMV 占线性求解时间 44%。

目标是在不改变迭代次数和位移结果的前提下降低单次 SpMV 时间。

2.2 基线 ​

示例数据:

指标基线
总求解时间100 s
CG 迭代数420
SpMV 总时间33.4 s
内存带宽165 GB/s
平台 STREAM 带宽190 GB/s
CPU IPC0.72
32 线程相对 16 线程加速1.05×

perf 显示 LLC Miss 高,PCM 显示带宽接近实际峰值。线程增加后时间不再下降,说明热点主要受带宽限制,而不是缺少核心。

2.3 假设 ​

矩阵来自三维位移问题,存在大量 3×3 节点块。CSR 为每个标量非零元保存列索引,索引流量较大。

假设:

转换为 3×3 BSR 可以让九个数值共享一次块列索引,并提高 x 向量连续访问,从而降低每次 SpMV 的字节数。

2.4 修改 ​

  • 保持自由度编号和 CG/AMG 参数不变;
  • 在矩阵构造阶段生成 3×3 BSR;
  • 对约束行使用明确的块内处理;
  • 添加 CSR 与 BSR 两个后端;
  • 使用相同初值和停止准则。

2.5 结果 ​

示例:

指标CSRBSR
CG 迭代数420420
SpMV 总时间33.4 s23.8 s
总求解时间100 s90.2 s
峰值矩阵内存8.6 GB7.4 GB
最大位移差—2.1×10⁻¹³

SpMV 提升约 1.40×,端到端只提升约 1.11×,符合 Amdahl 定律。不能宣传“求解器提升 40%”。

2.6 进一步实验 ​

  • 节点重编号是否改善 x 访问;
  • AMG 是否能直接使用块平滑器;
  • 约束较多或混合单元时 BSR 浪费多少;
  • 32 位块索引是否满足规模;
  • GPU BSR 与 CSR 哪个更适合实际行结构。

3. 案例二:应力恢复的 AoS 到 SoA ​

3.1 场景 ​

位移求解后,需要为大量积分点计算:

  • 六分量应力;
  • von Mises;
  • 多工况最大值。

该阶段数据并行度高,适合作为 [[/06-other/01-projects/profile/C++/2-StructKernelBench/2-StructKernelBench]] 的实践。

3.2 环境与基线 ​

示例实验固定:

text
CPU:双路 32 核,固定频率并绑定线程
构建:Clang Release,-O3,关闭 Fast-Math
输入:2,000 万积分点,六分量双精度应力
线程:1、8、16、32,使用相同静态分块
正确性:逐点相对误差 ≤ 1×10⁻¹²,包络索引完全一致
1
2
3
4
5
指标AoS 基线
应力恢复阶段2.80 s
von Mises 循环1.71 s
32 线程相对单线程加速9.4×
实际内存带宽118 GB/s
向量化 Lane 利用率43%

3.3 证据 ​

编译器报告显示 AoS 版本未充分向量化,因为循环只计算 von Mises,却以结构体形式读取全部字段并存在别名顾虑。

Advisor 显示:

  • Vectorization Efficiency 低;
  • 内存访问连续但每个点对象较大;
  • 该循环占后处理时间 61%。

3.4 修改 ​

text
AoS: stress[i].xx, stress[i].yy, ...
→
SoA: xx[i], yy[i], zz[i], xy[i], yz[i], zx[i]
1
2
3

同时:

  • 输出数组连续;
  • 外层按大块 OpenMP;
  • 避免循环内分配;
  • 为尾部使用编译器 Mask;
  • 保留双精度累加。

3.5 结果与验证 ​

示例结果:

指标AoSSoA
von Mises 循环1.71 s0.96 s
应力恢复阶段2.80 s2.02 s
32 线程相对单线程加速9.4×13.8×
向量化 Lane 利用率43%86%
最大逐点相对误差—3.7×10⁻¹⁴
包络索引差异数—0

同时检查:

  • 每点 von Mises 的最大绝对/相对误差;
  • NaN、Inf 和极端应力;
  • 多工况包络索引;
  • 单线程、不同线程数下结果;
  • 小规模和超出 LLC 的大规模。

3.6 结论 ​

如果小规模加速明显、大规模加速变小,可能是大规模开始受内存带宽限制。下一步应测带宽,而不是继续增加 SIMD 指令。

4. 案例三:CFD 面通量循环 ​

4.1 场景 ​

非结构网格有限体积求解器中,面循环:

cpp
for (Face f : internalFaces) {
    Cell o = owner[f];
    Cell n = neighbour[f];
    Flux flux = evaluate(state[o], state[n], geometry[f]);
    residual[o] += flux;
    residual[n] -= flux;
}
1
2
3
4
5
6
7

Nsight/CPU Profiler 显示它是主要热点之一。

4.2 环境与基线 ​

示例实验:

text
硬件:单路 32 核 CPU,256 GB 内存
构建:GCC Release,-O3,OpenMP
网格:2,400 万 Cell,7,100 万内部 Face
算法:稳态 SIMPLE,固定松弛和线性容差
正确性:质量不平衡 ≤ 1×10⁻⁶,阻力系数相对差 ≤ 1×10⁻⁸
1
2
3
4
5
指标基线
平均每次 SIMPLE 外迭代4.80 s
面通量循环1.60 s
压力求解2.64 s
分支预测失败率8.1%
LLC Miss Rate31%
收敛所需外迭代1,260

4.3 初始证据 ​

  • owner/neighbour 导致间接读取;
  • 对 residual 的双向散射产生并行写冲突;
  • 多种离散格式和限制器在循环内分支;
  • Face 顺序与 Cell 空间顺序无关;
  • 分支失败、LLC Miss 和原子冲突均较高。

4.4 不应一次做完的修改 ​

以下修改若同时进行,就无法判断收益来源:

  • Face 重排;
  • AoS 转 SoA;
  • 分离边界类型;
  • 原子改着色;
  • Kernel Fusion;
  • 精度变化。

应逐个建立实验。

4.5 实验 A:内部面与边界面分离 ​

目的:删除主内部面循环中的边界判断。

验证:

  • 所有边界仍被恰好处理一次;
  • 总质量通量不变;
  • 残差逐 Cell 等价;
  • 分支失败和循环时间变化。

如果分支下降但总时间不变,说明主要瓶颈可能仍是内存或原子。

4.6 实验 B:按 owner/空间局部性重排 Face ​

目的:让连续 Face 更可能复用 owner Cell 数据。

同时测:

  • L2/LLC Hit;
  • residual 写入局部性;
  • neighbour 访问是否恶化;
  • MPI 内部面/边界面分组是否受影响;
  • 梯度等其他面循环是否受益。

4.7 实验 C:原子与图着色 ​

比较:

方案关注点
原子冲突率、实现简单、颜色预处理为零
图着色同色无冲突、颜色同步、重排成本
Cell Gather无散射冲突、需要 Cell-Face 邻接
局部缓冲归并内存量和归并时间

稳态数千次迭代时,着色预处理容易摊销;一次性计算或网格频繁变化时不一定合适。

4.8 结果与数值验证 ​

依次实施“内部/边界分离”和“局部 Face 重排”,没有在同一实验中切换组装策略:

指标基线优化后
面通量循环1.60 s1.05 s
平均每次 SIMPLE 外迭代4.80 s4.21 s
分支预测失败率8.1%2.7%
LLC Miss Rate31%22%
收敛所需外迭代1,2601,260
最终质量不平衡4.2×10⁻⁷4.2×10⁻⁷
阻力系数相对差—6.1×10⁻¹⁰

另外逐 Cell 比较一次固定状态下的通量和残差,并确认:

  • 每个内部 Face 恰好贡献给 owner 和 neighbour;
  • 每个边界 Face 恰好处理一次;
  • 重排前后的残差范数和守恒量在双精度容差内一致;
  • 相同停止准则下残差历史没有系统性改变。

4.9 端到端限制 ​

假设通量循环快了 1.8×,但压力 AMG 仍占总时间 55%,端到端收益会受限。下一步应检查压力迭代数和 AMG,而不是继续深挖通量 Kernel。

5. 案例四:MPI 强扩展停止 ​

5.1 场景 ​

固定 2 亿 Cell 的 CFD 算例,从 64 Rank 扩展到 1024 Rank。

实验使用同一批计算节点、同一 MPI 和 Release 二进制;每节点 64 个 CPU 核,每个 Rank 固定一个核心集合。分区器版本、SIMPLE/AMG 参数、线性与外层停止准则保持不变。正确性标准为质量不平衡不超过 1×10⁻⁶、阻力系数相对 64 Rank 结果差异不超过 1×10⁻⁷。

示例数据:

Rank时间/步并行效率(相对 64)MPI 占比
648.0 s100%12%
1284.3 s93%17%
2562.4 s83%26%
5121.6 s63%41%
10241.35 s37%59%

1024 Rank 比 512 Rank 只快 1.19×,资源却增加一倍。

5.2 分解 ​

Score-P/Scalasca 显示:

  • Halo 消息数不变但每条消息更小;
  • Allreduce 延迟占比上升;
  • AMG 粗层大量 Rank 没有足够工作;
  • 最慢 Rank 的边界 Face 数明显高于平均;
  • 输出步骤出现共享文件系统等待。

5.3 优化假设 ​

按优先级:

  1. 重新分区,权重加入边界面和模型计算量;
  2. 合并多个 Field 的 Halo;
  3. 内部区域计算与 Halo 交换重叠;
  4. 减少或流水化 Krylov 全局归约;
  5. AMG 粗层收缩活动 Rank;
  6. 调整并行 I/O;
  7. 判断 512 Rank 是否已是经济停止点。

5.4 重叠实验 ​

text
Irecv/Isend
→ 内部 Cell/Face
→ Waitall
→ 分区边界 Cell/Face
1
2
3
4

测量不能只看 MPI_Waitall 下降,还要看:

  • 内部/边界拆分额外开销;
  • 总步骤时间;
  • MPI 是否有异步进展;
  • GPU 场景是否需要 CUDA-aware MPI;
  • Rank 不均是否仍决定总时间。

5.5 优化后结果与数值验证 ​

示例中采用加权重分区、Halo 字段合并和 AMG 粗层活动 Rank 收缩:

Rank优化前时间/步优化后时间/步优化后 MPI 占比
5121.60 s1.42 s34%
10241.35 s1.08 s46%

验证结果:

检查结果
1024 Rank 质量不平衡5.0×10⁻⁷
相对 64 Rank 阻力系数差2.8×10⁻⁹
压力方程平均迭代数差0
最终外迭代数差0
丢失或重复 Halo 实体0

还应比较各 Rank 的局部守恒和输出字段校验和,防止只看全局量时掩盖通信索引错误。

5.6 合理停止 ​

如果优化后 1024 Rank 的成本/步仍显著高于 512 Rank,且目标周转时间已满足,应选择 512 Rank。强扩展不是越大越好。

6. 案例五:迭代次数而不是 Kernel ​

6.1 现象 ​

新网格下总时间翻倍。Profiler 显示 SpMV 单次时间几乎不变,但压力方程迭代从 40 次增至 115 次。

6.2 错误方向 ​

  • 改写 SpMV;
  • 增加 GPU;
  • 关闭性能计时;
  • 降低收敛标准。

6.3 正确检查 ​

  • 网格非正交和长宽比;
  • 压力参考和边界;
  • 系数尺度;
  • AMG Coarsening 和 Smoother;
  • 非正交修正次数;
  • 分区导致的 AMG 质量变化;
  • 初值和时间步。

如果更强 AMG 让单次迭代贵 20%,但迭代从 115 降到 45,总时间仍会显著下降。这说明算法质量高于微观 Kernel 指标。

7. “求解器很慢”的决策树 ​

text
求解器很慢
│
├─ 输入、初始化或输出占主导?
│  ├─ 是 → 文件数量、格式、并行 I/O、输出频率
│  └─ 否
│
├─ 迭代/时间步/增量数量异常?
│  ├─ 是 → 网格、模型、离散、预条件、步长和收敛
│  └─ 否
│
├─ 单次迭代哪个阶段最大?
│  ├─ 组装/通量 → 数据布局、冲突、SIMD/GPU
│  ├─ 线性求解 → 矩阵性质、SpMV、预条件、归约
│  ├─ 通信 → 分区、消息、重叠、强扩展点
│  └─ I/O → 并行格式、批量写、异步
│
├─ CPU、GPU 或 MPI 证据是什么?
│  └─ 用对应工具验证体系结构机制
│
└─ 修改后
   ├─ 重新测量端到端
   ├─ 比较迭代次数
   ├─ 验证数值与工程量
   └─ 记录结论或撤销修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

8. 观察、解释与下一步 ​

观察到什么说明什么下一步检查什么
微基准提升远高于端到端提升Amdahl 定律或其他阶段成为新瓶颈阶段占比和优化后的时间线
单次迭代变快但总时间变慢迭代次数或 Setup 成本上升相同容差下的迭代历史和总成本
CPU Kernel 扩展到某线程数停止带宽、NUMA 或同步达到限制带宽曲线、远端页和线程等待
GPU Kernel 提升但求解器不变传输、主机工作或低调用占比主导H2D/D2H、CPU 空洞和调用次数
MPI Rank 增加后成本/步上升已越过经济强扩展点效率、资源成本和周转目标
性能提升但工程量漂移优化改变了精度、次序或停止路径守恒、残差、工程量和逐场差异

9. 最终检查表 ​

  • [ ] 优化目标是延迟、吞吐、规模、成本还是能耗?
  • [ ] 基线可重复且包含波动吗?
  • [ ] 最大热点占比足以产生端到端收益吗?
  • [ ] 证据能区分算法问题与实现问题吗?
  • [ ] 一次实验是否只有一个主要变量?
  • [ ] 是否记录线程、Rank、绑定和硬件?
  • [ ] 是否比较迭代次数与每迭代成本?
  • [ ] 是否检查守恒、平衡、残差和工程量?
  • [ ] 是否保存原始命令、日志和结果?
  • [ ] 是否知道收益停止增长的原因?

本专题完成。建议下一步按 [[/06-other/01-projects/profile/C++/2-StructKernelBench/2-StructKernelBench]] 建立可运行的结构算子实验,并把本章模板用于每一次性能改动。

下一篇:[[/06-other/01-projects/profile/C++/2-StructKernelBench/2-StructKernelBench]]

最后更新于:

Pager
上一篇7. 体系结构驱动优化:从性能证据到算法、数据与并行处理 / Architecture-Driven Optimization from Evidence to Algorithms and Parallelism

持续记录,持续成长

Copyright © Tidenflow