ThreatSourceLibaray/docs/test_result/performance_optimization_analysis.md

156 lines
4.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 威胁源仿真库对象池优化分析报告
## 优化尝试总结
### 背景
在发现`InfraredTargetRecognizer.cs`中存在LOH分配问题后我们实施了对象池优化试图通过池化大型数组来减少GC压力。
### 优化目标
- 消除640x480像素图像处理中的LOH分配约300KB/次)
- 减少Gen1/Gen2 GC频率
- 提升整体性能
## 实施的优化方案
### 1. 创建ImageProcessingPool
```csharp
// 实现了简化版对象池
public class ImageProcessingPool
{
private readonly ConcurrentBag<bool[,]> _visitedArrays = new();
private readonly ConcurrentBag<double[,]> _smokeIntensityArrays = new();
public bool[,] GetVisitedArray(int width, int height)
{
if (!_visitedArrays.TryTake(out var array))
{
array = new bool[_maxHeight, _maxWidth]; // 仍然LOH分配
}
return array;
}
public void ReturnVisitedArray(bool[,] array)
{
Array.Clear(array, 0, array.Length); // CPU密集操作
_visitedArrays.Add(array);
}
}
```
### 2. 修改图像处理代码
- `InfraredTargetRecognizer.SegmentTarget()` - 使用池化的visited数组
- `InfraredImageGenerator.GenerateImage()` - 使用池化的烟幕数组
## 性能测试结果对比
### 测试配置
- **场景**: 50个目标, 20个导弹, 30秒测试
- **帧率**: 50fps
- **模式**: Release
### 关键指标对比
| 指标 | 优化前 | 优化后 | 变化率 |
|------|--------|--------|-------|
| 平均帧时间 | 0.36ms | 2.97ms | +725% ❌ |
| 最大帧时间 | 20.57ms | 47.74ms | +132% ❌ |
| Gen0 GC频率 | 1.3次/秒 | 9.2次/秒 | +607% ❌ |
| Gen1 GC频率 | 1.1次/秒 | 8.5次/秒 | +673% ❌ |
| Gen2 GC频率 | 1.1次/秒 | 8.5次/秒 | +673% ❌ |
| 内存增长率 | 0.22MB/s | 0.46MB/s | +109% ❌ |
## 失败原因分析
### 1. **Array.Clear性能开销**
```csharp
// 对300KB数组的清理操作
Array.Clear(array, 0, 640 * 480); // 307,200次内存写入
```
**影响**: 每次归还数组都要进行大量内存写入CPU开销巨大
### 2. **ConcurrentBag同步开销**
```csharp
// 线程安全集合的性能代价
_visitedArrays.TryTake(out var array); // 同步操作
_visitedArrays.Add(array); // 同步操作
```
**影响**: 频繁的线程同步操作增加了额外开销
### 3. **对象池容量不足**
- 当池为空时仍然创建新的LOH对象
- 没有预热机制,启动时池为空
- 没有合适的容量规划
### 4. **过度设计**
- 简单的内存分配问题被复杂的对象池方案掩盖
- 引入了新的性能瓶颈
- 代码复杂性大幅增加
## 根本原因探索
### 原始性能好的真实原因
回顾最初的优秀性能测试结果0.36ms帧时间),可能的原因:
1. **Debug.WriteLine优化**: Release模式下完全消除
2. **LINQ优化**: 减少了临时集合分配
3. **测试场景差异**: 可能图像处理调用频率较低
### LOH分配的实际影响
虽然640x480数组确实会进入LOH
- **分配频率**: 可能并不像预期那样频繁
- **GC策略**: .NET的LOH管理已经相当优化
- **实际成本**: 对象池的管理成本可能超过了LOH分配成本
## 经验教训
### 1. **过早优化的代价**
- 在没有准确性能剖析的情况下就进行复杂优化
- 假设LOH分配是主要瓶颈但可能不是
### 2. **对象池的适用场景**
对象池适合:
- 高频分配的小到中等对象
- 创建成本高的对象
- 确定的容量需求
对象池不适合:
- 超大对象如300KB数组
- 低频使用的对象
- 需要频繁清理的对象
### 3. **性能优化的正确方法**
1. **先测量,后优化**
2. **一次只改变一个变量**
3. **用实际workload进行测试**
4. **考虑优化的总成本**
## 下一步建议
### 立即行动
1. **回滚对象池优化**,恢复到之前的良好性能
2. **保留其他有效优化**如LINQ消除、Debug.WriteLine处理
### 深入分析
1. **使用专业profiler**如JetBrains dotMemory, PerfView
2. **分析真实的内存分配热点**
3. **测量LOH分配的实际频率和影响**
### 替代方案
1. **减少图像处理频率**如每2-3帧处理一次
2. **降低图像分辨率**如320x240
3. **使用stackalloc for小数组**
4. **实现增量处理算法**
## 总结
这次对象池优化尝试虽然失败了,但提供了宝贵的经验:
1. **复杂的优化不一定更好**
2. **测量比假设更重要**
3. **.NET的内存管理已经相当优化**
4. **过度工程化可能适得其反**
最初的优秀性能0.36ms帧时间表明C#仿真库已经具备了良好的性能基础。我们应该专注于:
- 保持现有的良好性能
- 通过精确测量找到真正的瓶颈
- 采用简单而有效的优化策略