网格生成第一阶段,空间索引优化,性能提高3倍
This commit is contained in:
parent
6893b7efeb
commit
c3c1b8b994
@ -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坐标
|
||||
|
||||
288
doc/working/网格生成性能优化方案.md
Normal file
288
doc/working/网格生成性能优化方案.md
Normal 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. **空间哈希大小改变可能影响结果**
|
||||
- 解决方案:充分测试,确保结果一致性
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user