znlgis 博客

GIS开发与技术分享

第16章:性能优化、并发与最佳实践

OCCT 是一个庞大的算法库,既能处理数毫米的精密件,也要支撑数十万零件的装配。性能与稳定性是工程化部署的核心问题。本章总结性能调优思路、并发能力与开发最佳实践。

1. 测量先于优化

任何优化前先用 OSD_TimerOSD_Chronometer 测量:

OSD_Timer t; t.Start();
// 算法
t.Stop();
std::cout << "elapsed=" << t.ElapsedTime() << "s\n";

OCCT 还有:

  • Message_Report 自动收集步骤耗时;
  • Standard_Tools::WhatThread() 调试多线程;
  • Graphic3d_FrameStats 渲染帧率;
  • 外部工具 perf、vtune、Instruments。

2. 算法级优化

2.1 选择正确的数据结构

  • 拓扑遍历用 TopTools_IndexedMapOfShape 而非 std::set<TopoDS_Shape>
  • 频繁查找用 NCollection_DataMap + 自定义 Hasher;
  • 大量小对象用 NCollection_BaseAllocator 池分配器。

2.2 减少重复求交

  • 布尔运算 SetGlue(BOPAlgo_GlueShift|GlueFull) 跳过装配中“确知不相交”的零件;
  • 自定义 BRepBndLib 包围盒预筛;
  • 把多次 Cut(a, b)Cut(a, c) 合并为 BRepAlgoAPI_BuilderAlgo 多操作数版本。

2.3 让几何变简单

  • 解析几何替代 NURBS(GeomConvert::CurveToBSplineCurve 反操作 BRepLib::ConvertToCanonical);
  • ShapeUpgrade_UnifySameDomain 合并相同曲面的相邻面,减少边/面数量;
  • 删除冗余 Compound

2.4 控制容差

  • 拒绝把全局容差当 fuzzy 用;
  • 经过修复后再做布尔;
  • 必要时 ShapeFix_ShapeTolerance 限制最大值。

3. 并发能力

OCCT 多个核心算法支持并行(SetRunParallel(true)):

  • BOP(BRepAlgoAPI_*);
  • 网格化(BRepMesh_IncrementalMeshIMeshTools_Parameters::InParallel);
  • 圆角(部分算法);
  • 数据交换(STEPCAFControl_Reader::SetParallelRWGltf_CafReader)。

后端:

  • 内置 Standard_ThreadPool(OCCT 7.5+ 默认);
  • 可选 TBB(USE_TBB=ON);
  • 用户也可使用 OSD_Parallel::ForEach 并行循环:
    OSD_Parallel::ForEach(items.begin(), items.end(), [&](Item& it) { ... });
    

注意:

  • Handle(T) 引用计数线程安全;
  • 同一 TopoDS_Shape 的写操作不安全,需要拷贝;
  • AIS/V3d 必须在主线程使用;
  • OCAF 文档应在持有事务的线程中写入。

4. 内存管理

  • OCCT 大量使用引用计数 + 共享,多数情况下不需要手工释放;
  • 对“一次性”大数据,使用作用域 + Nullify() 提前释放;
  • NCollection_IncAllocator(增量分配器)适合算法局部数据;
  • 启用 MMGT_OPT=0/1/2 环境变量切换 OCCT 内部分配器(默认系统 malloc)。

5. 避免常见陷阱

  • 修改共享 TShape:先 BRepBuilderAPI_Copy
  • 跨线程操作 AIS:在主线程;
  • 频繁创建大 Compound:考虑层级聚合;
  • 重复网格化:调用 BRepTools::Clean 清旧;
  • 未关闭的 fuzzy=0 BOP:会因数值误差失败,必要时设小 fuzzy;
  • 未提供 PCurveBRepLib::BuildCurves3d 修补;
  • 大装配 BRepBndLib::Add(shape, bb, true):精确包围盒慢,必要时退化到默认。

6. 编译期优化

  • Release + LTO(GCC -flto、MSVC /GL)显著加速;
  • -O2 通常优于 -O3(OCCT 算法多分支不利于过度内联);
  • Windows 上勿混用 Debug runtime 与 Release lib,否则 Handle 引用计数会异常;
  • 使用 Ninja + ccache 加快迭代;
  • 大型应用按模块裁剪 OCCT,仅链接必要 toolkit。

7. 图形性能

  • BRepMesh 偏差合理:linear 0.05–0.5 mm;
  • 关闭 FaceBoundaryDraw 可大幅减线条;
  • Graphic3d_RenderingParams::FrustumCullingStateOcclusionCullingState
  • 使用 AIS_ConnectedInteractive 实例化重复零件;
  • 批量更新时 view->SetImmediateUpdate(false)
  • 复杂场景启用 Graphic3d_RenderingParams::ToReduceAA 简化反走样。

8. 异步与响应性

桌面应用中长时间算法应放后台线程,主线程负责进度显示与 UI 响应:

QtConcurrent::run([=]() {
    Message_ProgressScope sc(progressRange, "BOP", 1);
    BRepAlgoAPI_Cut cut(a, b);
    cut.SetRunParallel(true);
    cut.Build();
    // 发信号回主线程
});

Message_ProgressIndicator 通过 ProgressRange 提供分级进度,可在 Qt 中绑定到 QProgressBar

9. 发布与部署

  • Linux:patchelf/rpath 让二进制找到 OCCT 库;
  • Windows:拷贝 *.dll 与第三方依赖;
  • macOS:install_name_tool 修复 rpath;
  • 资源文件:CSF_* 环境变量保证 STEP/IGES/Plugin 配置可加载;
  • Web:Emscripten 编译,注意 Tcl 不可用。

10. 编码规范建议

  • 命名:类用 PascalCase,方法用 PascalCase,宏用 OCC_ 前缀;
  • 头文件:Module_Class.hxx、源文件:Module_Class.cxx
  • 句柄:始终 Handle(T) v = new T(...)
  • 异常:抛 Standard_Failure 派生类,捕获时使用 OCC_CATCH_SIGNALS
  • 字符串:TCollection_AsciiStringTCollection_ExtendedString 边界要清晰;
  • 文档:Doxygen 注释,遵循 dox/dev_guides/coding_rules

11. 测试与回归

  • 把核心算法用 Draw 脚本覆盖,纳入 tests/
  • C++ 集成测试用 GoogleTest,调用 OCCT API;
  • 性能测试基线:OSD_Timer + 历史快照对比;
  • 几何回归:BRepTools::Write 黄金 .brep 文件;
  • 持续集成:GitHub Actions / GitLab CI 并行多平台构建 + 测试。

12. 故障定位

  • Standard_FailureGetMessageString 含错误描述;
  • 内存崩溃:开 ASan/UBSan;
  • 死循环:OCC_CATCH_SIGNALS、Tcl 中 Ctrl+C
  • 几何异常:导出 .brep 复现,分享给 OCCT 社区或自查;
  • BOP 内部错误:开启 Message_Report 看 warnings。

13. 监控与日志

Message_Messenger 默认输出到 stdout;可注册自定义 Message_Printer 写到文件、网络或 GUI 控件。建议在产品级软件中:

  • 把 OCCT 警告/错误统一记录;
  • 区分 Trace/Info/Warning/Alarm/Fail
  • 开发态保留 Trace,生产态调高门槛。

14. 总结

性能与可靠性是 OCCT 应用的“两条腿”:

  • 性能依赖正确的数据结构、算法选择、并行化与内存管理;
  • 可靠性依赖容差控制、修复流程、异常处理与回归测试。

下一章我们看 OCCT 与外部生态的集成方式。