网格生成第一阶段,空间索引优化,性能提高3倍

This commit is contained in:
tian 2025-08-31 16:46:01 +08:00
parent 6893b7efeb
commit c3c1b8b994
4 changed files with 316 additions and 3 deletions

View File

@ -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坐标

View File

@ -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<string> GetSpatialHashKeysForPoint(Point3D point, double searchRadius)
{
var keys = new List<string>();
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<string, List<ModelItem>> _candidateCache =
new Dictionary<string, List<ModelItem>>();
private List<ModelItem> 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<ModelItem> 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<ModelItem>实现对象池
private readonly ObjectPool<HashSet<ModelItem>> _hashSetPool =
new ObjectPool<HashSet<ModelItem>>(
() => new HashSet<ModelItem>(),
set => set.Clear());
```
#### 2.3 批处理相邻点
```csharp
// 将相邻的网格点分组处理,共享候选列表
private void ProcessGridPointBatch(List<Point3D> 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<ModelItem> HeightFiltering(List<ModelItem> items, double minZ, double maxZ)
{
var filtered = new List<ModelItem>();
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<List<IntersectionResult>> threadLocalResults =
new ThreadLocal<List<IntersectionResult>>(() => new List<IntersectionResult>());
```
## 实施计划
### 立即实施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. **空间哈希大小改变可能影响结果**
- 解决方案:充分测试,确保结果一致性

View File

@ -32,7 +32,7 @@ namespace NavisworksTransport.PathPlanning
{
private readonly CategoryAttributeManager _categoryManager;
private readonly ChannelBasedGridBuilder _channelBuilder;
private readonly VerticalScanProcessor _verticalScanner;
private VerticalScanProcessor _verticalScanner;
/// <summary>
/// 构造函数
@ -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);

View File

@ -59,11 +59,23 @@ namespace NavisworksTransport.PathPlanning
/// <summary>
/// 构造函数
/// </summary>
/// <param name="spatialHashSize">空间哈希网格大小(米),默认10米</param>
/// <param name="spatialHashSize">空间哈希网格大小(米)或实际网格大小。如果<1米则视为网格大小并自动计算合适的空间哈希大小否则直接使用。默认10米</param>
/// <param name="parallelDegree">并行度默认为CPU核心数的一半避免过度并行导致崩溃</param>
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;