From c3c1b8b9947577772b5816b804703afb82d98051 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Sun, 31 Aug 2025 16:46:01 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BD=91=E6=A0=BC=E7=94=9F=E6=88=90=E7=AC=AC?= =?UTF-8?q?=E4=B8=80=E9=98=B6=E6=AE=B5=EF=BC=8C=E7=A9=BA=E9=97=B4=E7=B4=A2?= =?UTF-8?q?=E5=BC=95=E4=BC=98=E5=8C=96=EF=BC=8C=E6=80=A7=E8=83=BD=E6=8F=90?= =?UTF-8?q?=E9=AB=983=E5=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/requirement/todo_features.md | 5 + doc/working/网格生成性能优化方案.md | 288 ++++++++++++++++++++++ src/PathPlanning/GridMapGenerator.cs | 10 +- src/PathPlanning/VerticalScanProcessor.cs | 16 +- 4 files changed, 316 insertions(+), 3 deletions(-) create mode 100644 doc/working/网格生成性能优化方案.md diff --git a/doc/requirement/todo_features.md b/doc/requirement/todo_features.md index a6fff0a..d561c25 100644 --- a/doc/requirement/todo_features.md +++ b/doc/requirement/todo_features.md @@ -2,6 +2,11 @@ ## 功能点 +### [2025/08/31] + +1. [ ](性能优化)提高自动路径规划网格生成的速度(原速度:4楼模型,0.2米4.6秒;0.1米18秒) +优化空间索引后,0.2米网格,1.8秒;0.1米网格,5.1秒 + ### [2025/08/30] 1. [ ](性能优化)用几何方法识别通道的坡度变化(侧面上表面轮廓线),给通道网格准确的z坐标 diff --git a/doc/working/网格生成性能优化方案.md b/doc/working/网格生成性能优化方案.md new file mode 100644 index 0000000..c3d276f --- /dev/null +++ b/doc/working/网格生成性能优化方案.md @@ -0,0 +1,288 @@ +# 自动路径规划网格生成性能优化方案 + +## 当前性能问题 + +- 1万个模型,网格大小0.25m时需要5秒 +- 更大模型或更小网格时性能急剧下降 + +## 性能瓶颈分析 + +### 1. 空间索引构建阶段 (BuildSpatialHashIndex) + +- 需要遍历所有模型项(1万个)计算边界框 +- 每个模型项都要计算空间哈希键并添加到对应桶中 +- 固定的空间哈希大小(10m)可能导致分布不均 + +### 2. 垂直扫描阶段 (ParallelScanHeightIntervals) - **最大瓶颈** + +- 对每个网格点(网格0.25m时,数量巨大)执行: + - GetCandidateItemsFromSpatialHash: 查询9个相邻哈希桶 + - HeightFiltering: 高度范围筛选 + - PerformIntersectionTests: 对筛选后的每个模型项进行包围盒相交测试 +- 虽然使用了包围盒快速检测而非射线法,但重复查询和测试量仍然巨大 + +### 3. 关键问题 + +- 空间哈希粒度固定(10m),对于0.25m的网格太粗糙,导致每个桶内模型项过多 +- 每个网格点都要查询9个哈希桶,产生大量重复 +- HashSet去重和ToList转换带来额外开销 + +## 优化方案 + +### 第一阶段:空间索引优化(预期提升40-60%) + +#### 1.1 自适应空间哈希大小 + +```csharp +// 根据网格大小动态调整空间哈希大小 +// 当前固定10m,改为根据网格大小动态计算 +_spatialHashSize = Math.Max(gridSize * 4, 2.0); // 例如:0.25m网格用1m哈希桶 +``` + +#### 1.2 优化哈希键查询策略 + +- 对于小网格,减少查询的相邻桶数量(从9个减到4个或1个) +- 实现基于模型大小的智能查询范围 + +```csharp +private List GetSpatialHashKeysForPoint(Point3D point, double searchRadius) +{ + var keys = new List(); + int centerX = (int)Math.Floor(point.X / _spatialHashSize); + int centerY = (int)Math.Floor(point.Y / _spatialHashSize); + + // 根据搜索半径动态确定查询范围 + int range = (int)Math.Ceiling(searchRadius / _spatialHashSize); + range = Math.Min(range, 1); // 限制最大查询范围 + + for (int dx = -range; dx <= range; dx++) + { + for (int dy = -range; dy <= range; dy++) + { + keys.Add($"{centerX + dx},{centerY + dy}"); + } + } + return keys; +} +``` + +#### 1.3 缓存优化 + +- 为相邻网格点缓存候选模型列表 +- 实现LRU缓存减少重复查询 + +```csharp +private readonly Dictionary> _candidateCache = + new Dictionary>(); + +private List GetCandidateItemsFromSpatialHashWithCache(Point3D point) +{ + string cacheKey = $"{(int)(point.X/_spatialHashSize)},{(int)(point.Y/_spatialHashSize)}"; + + if (_candidateCache.TryGetValue(cacheKey, out var cached)) + return cached; + + var candidates = GetCandidateItemsFromSpatialHash(point); + _candidateCache[cacheKey] = candidates; + return candidates; +} +``` + +### 第二阶段:数据结构优化(预期提升20-30%) + +#### 2.1 避免不必要的数据转换 + +```csharp +// 直接返回IEnumerable而非ToList() +private IEnumerable GetCandidateItemsFromSpatialHash(Point3D point) +{ + var hashKeys = GetSpatialHashKeysForPoint(point); + + foreach (var key in hashKeys) + { + if (_spatialHashMap.TryGetValue(key, out var items)) + { + foreach (var item in items) + yield return item; + } + } +} +``` + +#### 2.2 使用对象池 + +```csharp +// 为HashSet实现对象池 +private readonly ObjectPool> _hashSetPool = + new ObjectPool>( + () => new HashSet(), + set => set.Clear()); +``` + +#### 2.3 批处理相邻点 + +```csharp +// 将相邻的网格点分组处理,共享候选列表 +private void ProcessGridPointBatch(List batch) +{ + // 计算批次的边界框 + var batchBounds = CalculateBounds(batch); + + // 一次性获取所有候选项 + var candidates = GetCandidatesForBounds(batchBounds); + + // 为批次中的每个点处理 + Parallel.ForEach(batch, point => + { + ProcessPointWithCandidates(point, candidates); + }); +} +``` + +### 第三阶段:算法优化(预期提升15-25%) + +#### 3.1 分层扫描策略 + +```csharp +// 先用粗网格(如1m)快速识别障碍物区域 +var coarseGrid = GenerateCoarseGrid(1.0); +var obstacleRegions = IdentifyObstacleRegions(coarseGrid); + +// 只在边界区域使用细网格(0.25m) +var fineGridPoints = GenerateFineGridPoints(obstacleRegions, 0.25); +``` + +#### 3.2 早期终止优化 + +```csharp +private List HeightFiltering(List items, double minZ, double maxZ) +{ + var filtered = new List(); + int fullyBlockedCount = 0; + + foreach (var item in items) + { + var bbox = item.BoundingBox(); + + // 如果完全覆盖扫描范围,标记并早期终止 + if (bbox.Min.Z <= minZ && bbox.Max.Z >= maxZ) + { + fullyBlockedCount++; + if (fullyBlockedCount > 3) // 多个障碍物完全覆盖 + { + return items; // 早期返回,无需继续筛选 + } + } + + if (bbox.Max.Z >= minZ && bbox.Min.Z <= maxZ) + { + filtered.Add(item); + } + } + + return filtered; +} +``` + +#### 3.3 空间局部性优化 + +```csharp +// 按空间顺序处理网格点,提高缓存命中率 +var sortedPoints = gridPoints + .OrderBy(p => p.Y) + .ThenBy(p => p.X) + .ToList(); +``` + +### 第四阶段:并行优化(预期提升10-20%) + +#### 4.1 优化并行粒度 + +```csharp +// 使用Partitioner创建更均衡的工作负载 +var partitioner = Partitioner.Create(pointsList, true); +Parallel.ForEach(partitioner, new ParallelOptions +{ + MaxDegreeOfParallelism = _parallelDegree +}, +gridPoint => +{ + // 处理逻辑 +}); +``` + +#### 4.2 减少锁竞争 + +```csharp +// 使用线程本地存储减少共享资源访问 +ThreadLocal> threadLocalResults = + new ThreadLocal>(() => new List()); +``` + +## 实施计划 + +### 立即实施(1-2天) + +1. 自适应空间哈希大小 +2. 优化哈希键查询策略 +3. 避免不必要的ToList()转换 + +### 短期实施(3-5天) + +1. 实现缓存机制 +2. 批处理相邻点 +3. 优化并行粒度 + +### 中期实施(1-2周) + +1. 分层扫描策略 +2. 对象池实现 +3. 早期终止优化 + +## 预期效果 + +| 场景 | 当前性能 | 优化后性能 | 提升比例 | +|------|---------|-----------|---------| +| 1万模型,0.25m网格 | 5秒 | 1.5-2秒 | 2.5-3倍 | +| 2万模型,0.25m网格 | 15-20秒 | 3-5秒 | 4-5倍 | +| 1万模型,0.1m网格 | 30秒+ | 5-8秒 | 4-6倍 | + +## 关键代码修改文件 + +1. **src/PathPlanning/VerticalScanProcessor.cs** + - 构造函数:添加网格大小参数 + - GetSpatialHashKeysForPoint:智能查询范围 + - GetCandidateItemsFromSpatialHash:缓存实现 + - ParallelScanHeightIntervals:批处理优化 + +2. **src/PathPlanning/GridMapGenerator.cs** + - GenerateChannelBased2_5D:传递网格大小到VerticalScanProcessor + +3. **src/PathPlanning/ChannelBasedGridBuilder.cs** + - BuildChannelCoverage:优化通道识别流程 + +## 测试验证 + +1. **性能测试** + - 不同模型数量(1千、5千、1万、2万) + - 不同网格大小(1m、0.5m、0.25m、0.1m) + - 记录时间和内存使用 + +2. **正确性验证** + - 对比优化前后的网格生成结果 + - 确保路径规划结果一致 + +3. **稳定性测试** + - 长时间运行测试 + - 内存泄漏检测 + +## 风险评估 + +1. **缓存可能导致内存增加** + - 解决方案:实现LRU缓存,限制缓存大小 + +2. **并行度提高可能导致CPU占用过高** + - 解决方案:提供可配置的并行度参数 + +3. **空间哈希大小改变可能影响结果** + - 解决方案:充分测试,确保结果一致性 diff --git a/src/PathPlanning/GridMapGenerator.cs b/src/PathPlanning/GridMapGenerator.cs index 2ea9ab6..fa281b6 100644 --- a/src/PathPlanning/GridMapGenerator.cs +++ b/src/PathPlanning/GridMapGenerator.cs @@ -32,7 +32,7 @@ namespace NavisworksTransport.PathPlanning { private readonly CategoryAttributeManager _categoryManager; private readonly ChannelBasedGridBuilder _channelBuilder; - private readonly VerticalScanProcessor _verticalScanner; + private VerticalScanProcessor _verticalScanner; /// /// 构造函数 @@ -239,6 +239,14 @@ namespace NavisworksTransport.PathPlanning // 2. 构建垂直扫描处理器的空间索引 LogManager.Info("[通道2.5D模式] 步骤2: 构建空间哈希索引"); + + // 根据实际网格大小重新创建垂直扫描器(仅当网格很小时) + if (cellSizeInModelUnits < 1.0) + { + LogManager.Info($"[通道2.5D模式] 检测到细网格 {cellSizeInModelUnits:F2},重新创建垂直扫描器以优化空间哈希"); + _verticalScanner = new VerticalScanProcessor(cellSizeInModelUnits); + } + var allItems = document.Models.RootItemDescendantsAndSelf; _verticalScanner.BuildSpatialHashIndex(allItems, bounds, channelCoverage.ChannelItems); diff --git a/src/PathPlanning/VerticalScanProcessor.cs b/src/PathPlanning/VerticalScanProcessor.cs index d90cc25..9f151e3 100644 --- a/src/PathPlanning/VerticalScanProcessor.cs +++ b/src/PathPlanning/VerticalScanProcessor.cs @@ -59,11 +59,23 @@ namespace NavisworksTransport.PathPlanning /// /// 构造函数 /// - /// 空间哈希网格大小(米),默认为10米 + /// 空间哈希网格大小(米)或实际网格大小。如果<1米则视为网格大小并自动计算合适的空间哈希大小,否则直接使用。默认10米 /// 并行度,默认为CPU核心数的一半,避免过度并行导致崩溃 public VerticalScanProcessor(double spatialHashSize = 10.0, int parallelDegree = 0) { - _spatialHashSize = spatialHashSize; + // 如果传入的是小于1的值,认为是网格大小,需要转换为合适的空间哈希大小 + // 否则直接使用传入的值(保持向后兼容) + if (spatialHashSize < 1.0) + { + // 网格大小 -> 空间哈希大小(使用8倍关系) + _spatialHashSize = Math.Max(spatialHashSize * 8, 2.0); + LogManager.Info($"[垂直扫描处理器] 根据网格大小 {spatialHashSize}m 自动调整空间哈希大小为 {_spatialHashSize}m"); + } + else + { + _spatialHashSize = spatialHashSize; + } + // 限制并行度,避免过度并行导致崩溃,最大为CPU核心数的一半 int maxParallelism = Math.Max(1, Environment.ProcessorCount / 2); _parallelDegree = parallelDegree > 0 ? Math.Min(parallelDegree, maxParallelism) : maxParallelism;