进行了部分性能优化,并记录了性能优化分析报告
This commit is contained in:
parent
33a2b47c6a
commit
388718b2b3
@ -0,0 +1,518 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using ThreatSource.Simulation;
|
||||
using ThreatSource.Equipment;
|
||||
using ThreatSource.Missile;
|
||||
using ThreatSource.Utils;
|
||||
using ThreatSource.Data;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace ThreatSource.Tests.Simulation
|
||||
{
|
||||
/// <summary>
|
||||
/// SimulationManager性能测试工具
|
||||
/// 专门用于测试内存分配风暴和GC压力
|
||||
/// </summary>
|
||||
public class SimulationManagerPerformanceTest
|
||||
{
|
||||
private readonly ITestOutputHelper _output;
|
||||
private readonly SimulationManager simulationManager;
|
||||
private readonly ThreatSourceDataManager dataManager;
|
||||
private readonly ThreatSourceFactory factory;
|
||||
private readonly List<string> entityIds;
|
||||
|
||||
// 性能测试参数
|
||||
private const int TARGET_COUNT = 50; // 目标数量
|
||||
private const int MISSILE_COUNT = 20; // 导弹数量
|
||||
private const int TEST_DURATION_SECONDS = 30; // 测试持续时间
|
||||
private const double TIME_STEP = 0.02; // 时间步长(50fps)
|
||||
|
||||
// 性能统计
|
||||
private long totalAllocations = 0;
|
||||
private long totalGCCollections = 0;
|
||||
private long peakMemoryUsage = 0;
|
||||
private double totalUpdateTime = 0;
|
||||
private int updateCount = 0;
|
||||
|
||||
public SimulationManagerPerformanceTest(ITestOutputHelper output)
|
||||
{
|
||||
_output = output;
|
||||
simulationManager = new SimulationManager();
|
||||
|
||||
// 使用与其他测试相同的路径解析方式
|
||||
string baseDir = AppDomain.CurrentDomain.BaseDirectory;
|
||||
string projectRoot = Path.GetFullPath(Path.Combine(baseDir, "../../../.."));
|
||||
string dataPath = Path.Combine(projectRoot, "ThreatSource/data");
|
||||
|
||||
dataManager = new ThreatSourceDataManager(dataPath);
|
||||
factory = new ThreatSourceFactory(dataManager, simulationManager);
|
||||
entityIds = new List<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 运行完整的性能测试 - 作为单元测试
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void RunPerformanceTest()
|
||||
{
|
||||
_output.WriteLine("=== SimulationManager性能测试 ===");
|
||||
_output.WriteLine($"测试参数: {TARGET_COUNT}个目标, {MISSILE_COUNT}个导弹, {TEST_DURATION_SECONDS}秒测试");
|
||||
_output.WriteLine($"时间步长: {TIME_STEP}s ({1.0/TIME_STEP:F0}fps)");
|
||||
_output.WriteLine("");
|
||||
|
||||
// 1. 准备测试环境
|
||||
SetupTestEnvironment();
|
||||
|
||||
// 2. 预热GC
|
||||
WarmupGC();
|
||||
|
||||
// 3. 记录基准内存状态
|
||||
var baselineMemory = RecordMemoryBaseline();
|
||||
|
||||
// 4. 运行核心性能测试
|
||||
var testResults = RunCorePerformanceTest();
|
||||
|
||||
// 5. 输出详细测试结果
|
||||
OutputDetailedResults(baselineMemory, testResults);
|
||||
|
||||
// 6. 清理测试环境
|
||||
CleanupTestEnvironment();
|
||||
|
||||
// 7. 性能断言
|
||||
AssertPerformanceThresholds(testResults);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 快速性能测试 - 用于持续集成
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void QuickPerformanceTest()
|
||||
{
|
||||
const int quickTargetCount = 10;
|
||||
const int quickMissileCount = 5;
|
||||
const int quickTestDuration = 5;
|
||||
|
||||
_output.WriteLine("=== SimulationManager快速性能测试 ===");
|
||||
_output.WriteLine($"测试参数: {quickTargetCount}个目标, {quickMissileCount}个导弹, {quickTestDuration}秒测试");
|
||||
|
||||
// 设置小规模测试环境
|
||||
SetupQuickTestEnvironment(quickTargetCount, quickMissileCount);
|
||||
|
||||
WarmupGC();
|
||||
var baselineMemory = RecordMemoryBaseline();
|
||||
var testResults = RunQuickPerformanceTest(quickTestDuration);
|
||||
|
||||
OutputDetailedResults(baselineMemory, testResults);
|
||||
CleanupTestEnvironment();
|
||||
|
||||
// 更宽松的性能要求
|
||||
Assert.True(testResults.AverageUpdateTime < 50.0, $"平均帧时间过长: {testResults.AverageUpdateTime:F2}ms");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置测试环境 - 创建大量实体
|
||||
/// </summary>
|
||||
private void SetupTestEnvironment()
|
||||
{
|
||||
_output.WriteLine("设置测试环境...");
|
||||
|
||||
// 添加天气
|
||||
var weather = factory.CreateWeather("sunny");
|
||||
simulationManager.SetWeather(weather);
|
||||
|
||||
// 创建目标
|
||||
for (int i = 0; i < TARGET_COUNT; i++)
|
||||
{
|
||||
var targetId = $"Target_{i}";
|
||||
var position = new Vector3D(
|
||||
Random.Shared.NextDouble() * 2000 - 1000, // -1000 to 1000
|
||||
1.2,
|
||||
Random.Shared.NextDouble() * 2000 - 1000 // -1000 to 1000
|
||||
);
|
||||
|
||||
var motionParams = new KinematicState
|
||||
{
|
||||
Position = position,
|
||||
Orientation = new Orientation(0, 0, 0),
|
||||
Speed = Random.Shared.NextDouble() * 5.0 // 0-5 m/s
|
||||
};
|
||||
|
||||
var target = factory.CreateEquipment(targetId, "mbt_001", motionParams);
|
||||
simulationManager.RegisterEntity(targetId, target);
|
||||
target.Activate();
|
||||
entityIds.Add(targetId);
|
||||
}
|
||||
|
||||
// 创建导弹
|
||||
for (int i = 0; i < MISSILE_COUNT; i++)
|
||||
{
|
||||
var missileId = $"Missile_{i}";
|
||||
var targetId = $"Target_{Random.Shared.Next(TARGET_COUNT)}"; // 随机选择目标
|
||||
|
||||
var position = new Vector3D(
|
||||
Random.Shared.NextDouble() * 4000 + 2000, // 2000-6000米距离
|
||||
Random.Shared.NextDouble() * 100 + 10, // 10-110米高度
|
||||
Random.Shared.NextDouble() * 200 - 100 // -100到100米侧向
|
||||
);
|
||||
|
||||
var motionParams = new KinematicState
|
||||
{
|
||||
Position = position,
|
||||
Orientation = new Orientation(Math.PI/2, Random.Shared.NextDouble() * 0.1 - 0.05, 0),
|
||||
Speed = Random.Shared.NextDouble() * 200 + 300 // 300-500 m/s
|
||||
};
|
||||
|
||||
// 随机选择导弹类型
|
||||
string[] missileTypes = { "lsgm_001", "itg_001", "mmw_001", "tsm_001" };
|
||||
string missileType = missileTypes[Random.Shared.Next(missileTypes.Length)];
|
||||
|
||||
var missile = factory.CreateMissile(missileId, missileType, targetId, motionParams);
|
||||
simulationManager.RegisterEntity(missileId, missile);
|
||||
missile.Activate();
|
||||
entityIds.Add(missileId);
|
||||
}
|
||||
|
||||
_output.WriteLine($"已创建 {TARGET_COUNT} 个目标和 {MISSILE_COUNT} 个导弹");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置快速测试环境
|
||||
/// </summary>
|
||||
private void SetupQuickTestEnvironment(int targetCount, int missileCount)
|
||||
{
|
||||
_output.WriteLine("设置快速测试环境...");
|
||||
|
||||
var weather = factory.CreateWeather("sunny");
|
||||
simulationManager.SetWeather(weather);
|
||||
|
||||
// 创建目标
|
||||
for (int i = 0; i < targetCount; i++)
|
||||
{
|
||||
var targetId = $"Target_{i}";
|
||||
var motionParams = new KinematicState
|
||||
{
|
||||
Position = new Vector3D(i * 100, 1.2, 0),
|
||||
Orientation = new Orientation(0, 0, 0),
|
||||
Speed = 2.0
|
||||
};
|
||||
|
||||
var target = factory.CreateEquipment(targetId, "mbt_001", motionParams);
|
||||
simulationManager.RegisterEntity(targetId, target);
|
||||
target.Activate();
|
||||
entityIds.Add(targetId);
|
||||
}
|
||||
|
||||
// 创建导弹
|
||||
for (int i = 0; i < missileCount; i++)
|
||||
{
|
||||
var missileId = $"Missile_{i}";
|
||||
var targetId = $"Target_{i % targetCount}";
|
||||
|
||||
var motionParams = new KinematicState
|
||||
{
|
||||
Position = new Vector3D(2000 + i * 100, 50, 0),
|
||||
Orientation = new Orientation(Math.PI/2, 0.05, 0),
|
||||
Speed = 400
|
||||
};
|
||||
|
||||
var missile = factory.CreateMissile(missileId, "lsgm_001", targetId, motionParams);
|
||||
simulationManager.RegisterEntity(missileId, missile);
|
||||
missile.Activate();
|
||||
entityIds.Add(missileId);
|
||||
}
|
||||
|
||||
_output.WriteLine($"已创建 {targetCount} 个目标和 {missileCount} 个导弹");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GC预热
|
||||
/// </summary>
|
||||
private void WarmupGC()
|
||||
{
|
||||
_output.WriteLine("GC预热中...");
|
||||
|
||||
// 强制进行几次GC以稳定内存状态
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
GC.Collect();
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
|
||||
_output.WriteLine("GC预热完成");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 记录内存基准线
|
||||
/// </summary>
|
||||
private MemorySnapshot RecordMemoryBaseline()
|
||||
{
|
||||
var baseline = new MemorySnapshot
|
||||
{
|
||||
WorkingSet = GC.GetTotalMemory(true),
|
||||
Gen0Collections = GC.CollectionCount(0),
|
||||
Gen1Collections = GC.CollectionCount(1),
|
||||
Gen2Collections = GC.CollectionCount(2)
|
||||
};
|
||||
|
||||
_output.WriteLine("内存基准线:");
|
||||
_output.WriteLine($" 工作集内存: {baseline.WorkingSet / 1024.0 / 1024.0:F2} MB");
|
||||
_output.WriteLine($" GC统计: Gen0={baseline.Gen0Collections}, Gen1={baseline.Gen1Collections}, Gen2={baseline.Gen2Collections}");
|
||||
_output.WriteLine("");
|
||||
|
||||
return baseline;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 运行核心性能测试
|
||||
/// </summary>
|
||||
private TestResults RunCorePerformanceTest()
|
||||
{
|
||||
return RunPerformanceTestCore(TEST_DURATION_SECONDS);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 运行快速性能测试
|
||||
/// </summary>
|
||||
private TestResults RunQuickPerformanceTest(int duration)
|
||||
{
|
||||
return RunPerformanceTestCore(duration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 核心性能测试逻辑
|
||||
/// </summary>
|
||||
private TestResults RunPerformanceTestCore(int durationSeconds)
|
||||
{
|
||||
_output.WriteLine("开始核心性能测试...");
|
||||
|
||||
var results = new TestResults();
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
var memorySnapshots = new List<MemorySnapshot>();
|
||||
var updateTimes = new List<double>();
|
||||
|
||||
// 启动仿真
|
||||
simulationManager.StartSimulation(TIME_STEP);
|
||||
|
||||
var testStartTime = DateTime.UtcNow;
|
||||
var nextSnapshotTime = testStartTime.AddSeconds(1);
|
||||
|
||||
while (stopwatch.Elapsed.TotalSeconds < durationSeconds)
|
||||
{
|
||||
var updateStopwatch = Stopwatch.StartNew();
|
||||
|
||||
// 这是我们要测试的核心代码路径
|
||||
simulationManager.Update(TIME_STEP);
|
||||
|
||||
updateStopwatch.Stop();
|
||||
var updateTime = updateStopwatch.Elapsed.TotalMilliseconds;
|
||||
updateTimes.Add(updateTime);
|
||||
|
||||
totalUpdateTime += updateTime;
|
||||
updateCount++;
|
||||
|
||||
// 每秒记录一次内存快照
|
||||
if (DateTime.UtcNow >= nextSnapshotTime)
|
||||
{
|
||||
var snapshot = new MemorySnapshot
|
||||
{
|
||||
Timestamp = DateTime.UtcNow,
|
||||
WorkingSet = GC.GetTotalMemory(false),
|
||||
Gen0Collections = GC.CollectionCount(0),
|
||||
Gen1Collections = GC.CollectionCount(1),
|
||||
Gen2Collections = GC.CollectionCount(2)
|
||||
};
|
||||
memorySnapshots.Add(snapshot);
|
||||
nextSnapshotTime = nextSnapshotTime.AddSeconds(1);
|
||||
|
||||
// 实时输出进度
|
||||
_output.WriteLine($"[{stopwatch.Elapsed.TotalSeconds:F1}s] " +
|
||||
$"内存: {snapshot.WorkingSet / 1024.0 / 1024.0:F1}MB, " +
|
||||
$"平均帧时间: {updateTimes.TakeLast(50).Average():F2}ms");
|
||||
}
|
||||
|
||||
// 模拟真实帧率
|
||||
var frameTime = (int)(TIME_STEP * 1000);
|
||||
var elapsed = (int)updateStopwatch.Elapsed.TotalMilliseconds;
|
||||
if (elapsed < frameTime)
|
||||
{
|
||||
Thread.Sleep(frameTime - elapsed);
|
||||
}
|
||||
}
|
||||
|
||||
stopwatch.Stop();
|
||||
simulationManager.StopSimulation();
|
||||
|
||||
results.TotalTestTime = stopwatch.Elapsed.TotalSeconds;
|
||||
results.MemorySnapshots = memorySnapshots;
|
||||
results.UpdateTimes = updateTimes;
|
||||
results.TotalUpdates = updateCount;
|
||||
results.AverageUpdateTime = totalUpdateTime / updateCount;
|
||||
|
||||
_output.WriteLine("核心性能测试完成");
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 输出详细测试结果
|
||||
/// </summary>
|
||||
private void OutputDetailedResults(MemorySnapshot baseline, TestResults results)
|
||||
{
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine("=== 详细性能测试结果 ===");
|
||||
|
||||
// 基本统计
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine("【基本统计】");
|
||||
_output.WriteLine($"测试时长: {results.TotalTestTime:F1} 秒");
|
||||
_output.WriteLine($"总更新次数: {results.TotalUpdates}");
|
||||
_output.WriteLine($"平均FPS: {results.TotalUpdates / results.TotalTestTime:F1}");
|
||||
_output.WriteLine($"平均帧时间: {results.AverageUpdateTime:F2} ms");
|
||||
|
||||
// 帧时间分析
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine("【帧时间分析】");
|
||||
var sortedTimes = results.UpdateTimes.OrderBy(x => x).ToList();
|
||||
_output.WriteLine($"最小帧时间: {sortedTimes.First():F2} ms");
|
||||
_output.WriteLine($"最大帧时间: {sortedTimes.Last():F2} ms");
|
||||
_output.WriteLine($"95%分位数: {sortedTimes[(int)(sortedTimes.Count * 0.95)]:F2} ms");
|
||||
_output.WriteLine($"99%分位数: {sortedTimes[(int)(sortedTimes.Count * 0.99)]:F2} ms");
|
||||
|
||||
// 内存分析
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine("【内存使用分析】");
|
||||
var finalSnapshot = results.MemorySnapshots.Last();
|
||||
var memoryIncrease = finalSnapshot.WorkingSet - baseline.WorkingSet;
|
||||
var peakMemory = results.MemorySnapshots.Max(s => s.WorkingSet);
|
||||
|
||||
_output.WriteLine($"起始内存: {baseline.WorkingSet / 1024.0 / 1024.0:F2} MB");
|
||||
_output.WriteLine($"结束内存: {finalSnapshot.WorkingSet / 1024.0 / 1024.0:F2} MB");
|
||||
_output.WriteLine($"峰值内存: {peakMemory / 1024.0 / 1024.0:F2} MB");
|
||||
_output.WriteLine($"内存增长: {memoryIncrease / 1024.0 / 1024.0:F2} MB");
|
||||
_output.WriteLine($"平均内存增长率: {(memoryIncrease / results.TotalTestTime) / 1024.0 / 1024.0:F2} MB/s");
|
||||
|
||||
// GC分析
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine("【垃圾回收分析】");
|
||||
var gen0Increase = finalSnapshot.Gen0Collections - baseline.Gen0Collections;
|
||||
var gen1Increase = finalSnapshot.Gen1Collections - baseline.Gen1Collections;
|
||||
var gen2Increase = finalSnapshot.Gen2Collections - baseline.Gen2Collections;
|
||||
|
||||
_output.WriteLine($"Gen0 GC次数: {gen0Increase} ({gen0Increase / results.TotalTestTime:F1} 次/秒)");
|
||||
_output.WriteLine($"Gen1 GC次数: {gen1Increase} ({gen1Increase / results.TotalTestTime:F1} 次/秒)");
|
||||
_output.WriteLine($"Gen2 GC次数: {gen2Increase} ({gen2Increase / results.TotalTestTime:F1} 次/秒)");
|
||||
_output.WriteLine($"总GC次数: {gen0Increase + gen1Increase + gen2Increase}");
|
||||
|
||||
// 性能评级
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine("【性能评级】");
|
||||
var frameTimeGrade = GetPerformanceGrade(results.AverageUpdateTime, 20.0, 50.0); // 20ms=50fps, 50ms=20fps
|
||||
var memoryGrade = GetPerformanceGrade(memoryIncrease / 1024.0 / 1024.0, 10.0, 50.0); // 10MB, 50MB
|
||||
var gcGrade = GetPerformanceGrade(gen0Increase / results.TotalTestTime, 1.0, 5.0); // 1次/秒, 5次/秒
|
||||
|
||||
_output.WriteLine($"帧时间表现: {frameTimeGrade}");
|
||||
_output.WriteLine($"内存管理: {memoryGrade}");
|
||||
_output.WriteLine($"GC频率: {gcGrade}");
|
||||
|
||||
// 热点分析提示
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine("【潜在问题分析】");
|
||||
if (results.AverageUpdateTime > 20.0)
|
||||
{
|
||||
_output.WriteLine("⚠️ 平均帧时间超过20ms,可能存在性能瓶颈");
|
||||
}
|
||||
if (gen0Increase / results.TotalTestTime > 2.0)
|
||||
{
|
||||
_output.WriteLine("⚠️ Gen0 GC频率过高,存在内存分配风暴");
|
||||
}
|
||||
if (memoryIncrease > 50 * 1024 * 1024)
|
||||
{
|
||||
_output.WriteLine("⚠️ 内存增长过多,可能存在内存泄漏");
|
||||
}
|
||||
if (sortedTimes.Last() > sortedTimes[(int)(sortedTimes.Count * 0.95)] * 2)
|
||||
{
|
||||
_output.WriteLine("⚠️ 存在显著的帧时间波动,GC暂停可能较严重");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 性能断言 - 确保性能在可接受范围内
|
||||
/// </summary>
|
||||
private void AssertPerformanceThresholds(TestResults results)
|
||||
{
|
||||
// 这些阈值是基于现有代码的性能问题设定的
|
||||
// 优化后这些阈值应该更严格
|
||||
Assert.True(results.AverageUpdateTime < 100.0, $"平均帧时间过长: {results.AverageUpdateTime:F2}ms");
|
||||
|
||||
var finalSnapshot = results.MemorySnapshots.Last();
|
||||
var baseline = results.MemorySnapshots.First();
|
||||
var gcIncrease = (finalSnapshot.Gen0Collections - baseline.Gen0Collections) / results.TotalTestTime;
|
||||
|
||||
// 如果GC频率超过10次/秒,说明内存分配风暴很严重
|
||||
Assert.True(gcIncrease < 10.0, $"Gen0 GC频率过高: {gcIncrease:F1} 次/秒");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取性能评级
|
||||
/// </summary>
|
||||
private string GetPerformanceGrade(double value, double goodThreshold, double badThreshold)
|
||||
{
|
||||
if (value <= goodThreshold) return "优秀 ✅";
|
||||
if (value <= badThreshold) return "一般 ⚠️";
|
||||
return "较差 ❌";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理测试环境
|
||||
/// </summary>
|
||||
private void CleanupTestEnvironment()
|
||||
{
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine("清理测试环境...");
|
||||
|
||||
foreach (var entityId in entityIds)
|
||||
{
|
||||
simulationManager.UnregisterEntity(entityId);
|
||||
}
|
||||
|
||||
entityIds.Clear();
|
||||
simulationManager.StopSimulation();
|
||||
|
||||
// 强制GC清理
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
GC.Collect();
|
||||
|
||||
_output.WriteLine("清理完成");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 内存快照数据
|
||||
/// </summary>
|
||||
public class MemorySnapshot
|
||||
{
|
||||
public DateTime Timestamp { get; set; }
|
||||
public long WorkingSet { get; set; }
|
||||
public int Gen0Collections { get; set; }
|
||||
public int Gen1Collections { get; set; }
|
||||
public int Gen2Collections { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试结果数据
|
||||
/// </summary>
|
||||
public class TestResults
|
||||
{
|
||||
public double TotalTestTime { get; set; }
|
||||
public List<MemorySnapshot> MemorySnapshots { get; set; } = new();
|
||||
public List<double> UpdateTimes { get; set; } = new();
|
||||
public int TotalUpdates { get; set; }
|
||||
public double AverageUpdateTime { get; set; }
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,5 @@
|
||||
using System.Text;
|
||||
|
||||
namespace ThreatSource.Simulation
|
||||
{
|
||||
/// <summary>
|
||||
@ -5,6 +7,10 @@ namespace ThreatSource.Simulation
|
||||
/// </summary>
|
||||
public class ElementStatusInfo
|
||||
{
|
||||
// 简单的StringBuilder对象池(线程安全)
|
||||
private static readonly ThreadLocal<StringBuilder> _stringBuilderCache =
|
||||
new ThreadLocal<StringBuilder>(() => new StringBuilder(512));
|
||||
|
||||
/// <summary>
|
||||
/// 仿真元素的唯一标识符
|
||||
/// </summary>
|
||||
@ -55,34 +61,39 @@ namespace ThreatSource.Simulation
|
||||
/// <returns>状态的字符串表示</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
string baseInfo = $"[{ElementType}] Id={Id}, Active={IsActive}, 位置={KState.Position}, 方向={KState.Orientation}, 速度={KState.Speed:F2}, 速度矢量={KState.Velocity}";
|
||||
// 使用线程本地的StringBuilder缓存
|
||||
var sb = _stringBuilderCache.Value!;
|
||||
sb.Clear(); // 清空之前的内容
|
||||
|
||||
sb.Append('[').Append(ElementType).Append("] Id=").Append(Id)
|
||||
.Append(", Active=").Append(IsActive)
|
||||
.Append(", 位置=").Append(KState.Position)
|
||||
.Append(", 方向=").Append(KState.Orientation)
|
||||
.Append(", 速度=").Append(KState.Speed.ToString("F2"))
|
||||
.Append(", 速度矢量=").Append(KState.Velocity);
|
||||
|
||||
if (ExtendedProperties.Count > 0)
|
||||
{
|
||||
string extendedInfo = string.Join(", ", ExtendedProperties.Select(p =>
|
||||
sb.Append(", ");
|
||||
bool first = true;
|
||||
foreach (var kvp in ExtendedProperties)
|
||||
{
|
||||
string valueStr;
|
||||
if (p.Value is double d)
|
||||
if (!first) sb.Append(", ");
|
||||
sb.Append(kvp.Key).Append('=');
|
||||
|
||||
if (kvp.Value is double d)
|
||||
{
|
||||
if (d > 0 && d < 1e-4)
|
||||
{
|
||||
valueStr = d.ToString("E2");
|
||||
}
|
||||
else
|
||||
{
|
||||
valueStr = d.ToString("F2");
|
||||
}
|
||||
sb.Append(d > 0 && d < 1e-4 ? d.ToString("E2") : d.ToString("F2"));
|
||||
}
|
||||
else
|
||||
{
|
||||
valueStr = p.Value?.ToString() ?? "null"; // 处理可能的null值
|
||||
sb.Append(kvp.Value?.ToString() ?? "null");
|
||||
}
|
||||
return $"{p.Key}={valueStr}";
|
||||
}));
|
||||
return $"{baseInfo}, {extendedInfo}";
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
return baseInfo;
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -81,6 +81,13 @@ namespace ThreatSource.Simulation
|
||||
/// </summary>
|
||||
public Weather CurrentWeather => _currentWeather;
|
||||
|
||||
/// 新增:重用的Random实例和临时集合
|
||||
private readonly Random _random = new();
|
||||
private readonly List<SimulationElement> _tempActiveElements = new(1000);
|
||||
private readonly List<BaseMissile> _tempActiveMissiles = new(100);
|
||||
private readonly List<Tank> _tempActiveTargets = new(100);
|
||||
private readonly List<(Tank tank, BaseMissile missile, double damage)> _tempHitEvents = new(50);
|
||||
|
||||
/// <summary>
|
||||
/// 启动仿真系统
|
||||
/// </summary>
|
||||
@ -143,26 +150,31 @@ namespace ThreatSource.Simulation
|
||||
|
||||
private void UpdateEntities(double deltaTime)
|
||||
{
|
||||
// 创建活跃实体的快照
|
||||
List<SimulationElement> activeElements;
|
||||
// 清空并重用临时集合
|
||||
_tempActiveElements.Clear();
|
||||
|
||||
// 收集活跃实体(避免LINQ和ToList)
|
||||
lock (_lock)
|
||||
{
|
||||
activeElements = entities.Values
|
||||
.Cast<SimulationElement>()
|
||||
.Where(e => e.IsActive)
|
||||
.ToList();
|
||||
foreach (var entity in entities.Values)
|
||||
{
|
||||
if (entity is SimulationElement element && element.IsActive)
|
||||
{
|
||||
_tempActiveElements.Add(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 使用快照更新实体
|
||||
foreach (var element in activeElements)
|
||||
// 使用for循环更新实体,避免foreach枚举器分配
|
||||
for (int i = 0; i < _tempActiveElements.Count; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
element.Update(deltaTime);
|
||||
_tempActiveElements[i].Update(deltaTime);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"更新实体 {element.Id} 时发生错误: {ex.Message}");
|
||||
Debug.WriteLine($"更新实体 {_tempActiveElements[i].Id} 时发生错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -172,26 +184,45 @@ namespace ThreatSource.Simulation
|
||||
{
|
||||
try
|
||||
{
|
||||
// 创建活跃导弹和坦克的快照
|
||||
var activeMissiles = entities.Values.OfType<BaseMissile>().Where(e => e.IsActive).ToList();
|
||||
var activeTargets = entities.Values.OfType<Tank>().Where(e => e.IsActive).ToList();
|
||||
var hitEvents = new List<(Tank tank, BaseMissile missile, double damage)>();
|
||||
// 清空并重用临时集合
|
||||
_tempActiveMissiles.Clear();
|
||||
_tempActiveTargets.Clear();
|
||||
_tempHitEvents.Clear();
|
||||
|
||||
Debug.WriteLine($"活动导弹数量: {activeMissiles.Count}");
|
||||
Debug.WriteLine($"活动目标数量: {activeTargets.Count}");
|
||||
|
||||
// 收集所有的命中信息
|
||||
foreach (var missile in activeMissiles)
|
||||
// 收集活跃导弹和目标(避免LINQ)
|
||||
lock (_lock)
|
||||
{
|
||||
foreach (var target in activeTargets)
|
||||
foreach (var entity in entities.Values)
|
||||
{
|
||||
switch (entity)
|
||||
{
|
||||
case BaseMissile missile when missile.IsActive:
|
||||
_tempActiveMissiles.Add(missile);
|
||||
break;
|
||||
case Tank tank when tank.IsActive:
|
||||
_tempActiveTargets.Add(tank);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Debug.WriteLine($"活动导弹数量: {_tempActiveMissiles.Count}");
|
||||
Debug.WriteLine($"活动目标数量: {_tempActiveTargets.Count}");
|
||||
|
||||
// 检测命中(使用for循环避免枚举器分配)
|
||||
for (int i = 0; i < _tempActiveMissiles.Count; i++)
|
||||
{
|
||||
var missile = _tempActiveMissiles[i];
|
||||
for (int j = 0; j < _tempActiveTargets.Count; j++)
|
||||
{
|
||||
var target = _tempActiveTargets[j];
|
||||
double distance = Vector3D.Distance(missile.KState.Position, target.KState.Position);
|
||||
Debug.WriteLine($"导弹 {missile.Id} 和目标 {target.Id} 之间的距离: {distance:F2}m");
|
||||
|
||||
if (distance <= missile.Properties.ExplosionRadius)
|
||||
{
|
||||
// 使用命中概率进行随机判定
|
||||
var random = new Random();
|
||||
double randomValue = random.NextDouble(); // 生成0.0到1.0的随机数
|
||||
// 使用重用的Random实例
|
||||
double randomValue = _random.NextDouble();
|
||||
|
||||
Debug.WriteLine($"导弹 {missile.Id} 命中概率判定: 随机值={randomValue:F3}, 命中概率={missile.Properties.HitProbability:F3}");
|
||||
|
||||
@ -199,7 +230,7 @@ namespace ThreatSource.Simulation
|
||||
{
|
||||
Debug.WriteLine($"导弹 {missile.Id} 命中概率判定成功,将造成伤害");
|
||||
double damage = CalculateMissileDamage(missile);
|
||||
hitEvents.Add((target, missile, damage));
|
||||
_tempHitEvents.Add((target, missile, damage));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -210,9 +241,10 @@ namespace ThreatSource.Simulation
|
||||
}
|
||||
}
|
||||
|
||||
// 先处理所有伤害
|
||||
foreach (var (tank, missile, damage) in hitEvents)
|
||||
// 处理所有伤害
|
||||
for (int i = 0; i < _tempHitEvents.Count; i++)
|
||||
{
|
||||
var (tank, missile, damage) = _tempHitEvents[i];
|
||||
if (tank.IsActive)
|
||||
{
|
||||
tank.TakeDamage(damage);
|
||||
@ -220,9 +252,10 @@ namespace ThreatSource.Simulation
|
||||
}
|
||||
}
|
||||
|
||||
// 最后一次性发布所有命中事件
|
||||
foreach (var (tank, missile, _) in hitEvents)
|
||||
// 发布所有命中事件
|
||||
for (int i = 0; i < _tempHitEvents.Count; i++)
|
||||
{
|
||||
var (tank, missile, _) = _tempHitEvents[i];
|
||||
try
|
||||
{
|
||||
PublishEvent(new TargetHitEvent { TargetId = tank.Id, MissileId = missile.Id });
|
||||
@ -279,13 +312,13 @@ namespace ThreatSource.Simulation
|
||||
if (eventHandlers.TryGetValue(actualType, out var actualHandlers))
|
||||
{
|
||||
Debug.WriteLine($"[事件] 找到 {actualHandlers.Count} 个处理器(实际类型)");
|
||||
// 创建处理器列表的副本
|
||||
var handlers = actualHandlers.ToList();
|
||||
foreach (var handler in handlers)
|
||||
// 使用for循环避免枚举器分配和ToList()调用
|
||||
int handlerCount = actualHandlers.Count;
|
||||
for (int i = 0; i < handlerCount; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
handler.DynamicInvoke(evt);
|
||||
actualHandlers[i].DynamicInvoke(evt);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -299,13 +332,20 @@ namespace ThreatSource.Simulation
|
||||
if (eventHandlers.TryGetValue(eventType, out var typeHandlers))
|
||||
{
|
||||
Debug.WriteLine($"[事件] 找到 {typeHandlers.Count} 个处理器(声明类型)");
|
||||
// 创建处理器列表的副本
|
||||
var handlers = typeHandlers.ToList();
|
||||
foreach (var handler in handlers)
|
||||
// 使用for循环避免枚举器分配和ToList()调用
|
||||
int handlerCount = typeHandlers.Count;
|
||||
for (int i = 0; i < handlerCount; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
((Action<T>)handler).Invoke(evt);
|
||||
if (typeHandlers[i] is Action<T> typedHandler)
|
||||
{
|
||||
typedHandler.Invoke(evt);
|
||||
}
|
||||
else
|
||||
{
|
||||
typeHandlers[i].DynamicInvoke(evt);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
129
docs/performance_baseline_report_2025-06-04.md
Normal file
129
docs/performance_baseline_report_2025-06-04.md
Normal file
@ -0,0 +1,129 @@
|
||||
# ThreatSource项目性能基准测试报告
|
||||
|
||||
**测试日期**: 2025年6月4日
|
||||
**测试版本**: 当前开发版本
|
||||
**测试环境**: macOS (Apple Silicon)
|
||||
|
||||
## 📊 测试概述
|
||||
|
||||
本次测试旨在建立ThreatSource仿真库的性能基准,识别关键性能瓶颈,为后续优化工作提供数据支撑。
|
||||
|
||||
### 测试配置
|
||||
- **测试规模**: 50个目标实体 + 20个导弹实体
|
||||
- **测试时长**: 30秒
|
||||
- **目标帧率**: 50 FPS (时间步长0.02s)
|
||||
- **测试模式**: 实时仿真模式
|
||||
|
||||
## 🎯 关键性能指标
|
||||
|
||||
### 帧率性能
|
||||
| 指标 | 数值 | 目标值 | 达成率 |
|
||||
|------|------|--------|--------|
|
||||
| 平均FPS | 41.7 | 50.0 | 83.4% |
|
||||
| 平均帧时间 | 9.32ms | 20ms | ✅ 优秀 |
|
||||
| 最小帧时间 | 0.20ms | - | - |
|
||||
| 最大帧时间 | 178.34ms | <50ms | ❌ 严重超标 |
|
||||
| 95%分位数 | 40.21ms | <20ms | ❌ 需优化 |
|
||||
| 99%分位数 | 57.25ms | <30ms | ❌ 需优化 |
|
||||
|
||||
### 内存使用情况
|
||||
| 指标 | 数值 | 评估 |
|
||||
|------|------|------|
|
||||
| 起始内存 | 2.85MB | 基线良好 |
|
||||
| 结束内存 | 11.97MB | 增长适中 |
|
||||
| 峰值内存 | 14.25MB | 可接受 |
|
||||
| 内存增长 | 9.13MB | 增长率0.30MB/s |
|
||||
|
||||
### 垃圾回收分析 ⚠️
|
||||
| GC代数 | 总次数 | 频率(次/秒) | 阈值 | 状态 |
|
||||
|--------|--------|-------------|------|------|
|
||||
| Gen0 | 561 | 18.7 | <5.0 | ❌ 严重超标 |
|
||||
| Gen1 | 478 | 15.9 | <2.0 | ❌ 严重超标 |
|
||||
| Gen2 | 477 | 15.9 | <0.5 | ❌ 严重超标 |
|
||||
| **总计** | **1516** | **50.5** | **<8.0** | **❌ 内存分配风暴** |
|
||||
|
||||
## 📈 性能时序分析
|
||||
|
||||
测试过程中观察到明显的性能波动模式:
|
||||
|
||||
**第1-15秒**: 帧时间波动剧烈 (5-43ms)
|
||||
- 存在显著GC暂停
|
||||
- 帧时间不稳定
|
||||
|
||||
**第16-30秒**: 性能相对稳定 (0.8-1.1ms)
|
||||
- 可能进入稳态运行
|
||||
- 但内存使用仍在波动
|
||||
|
||||
## 🔍 问题根因分析
|
||||
|
||||
### 1. 内存分配风暴
|
||||
- **症状**: Gen0 GC频率18.7次/秒,远超正常范围
|
||||
- **影响**: 导致频繁的GC暂停,帧时间大幅波动
|
||||
- **可能原因**:
|
||||
- SimulationManager中大量临时对象创建
|
||||
- LINQ操作产生中间集合
|
||||
- 红外图像处理过程中的数组分配
|
||||
|
||||
### 2. GC暂停导致的帧时间跳跃
|
||||
- **症状**: 帧时间从0.2ms跳跃到178ms
|
||||
- **影响**: 严重影响实时仿真的流畅性
|
||||
- **根因**: 频繁的Gen1/Gen2 GC触发
|
||||
|
||||
### 3. 内存使用模式不优
|
||||
- **症状**: 内存使用呈锯齿状波动
|
||||
- **影响**: 触发更多GC周期
|
||||
- **根因**: 缺乏对象重用机制
|
||||
|
||||
## 🎯 优化建议
|
||||
|
||||
### 短期目标 (1-2周)
|
||||
1. **实现对象池模式**: 针对高频创建的临时对象
|
||||
2. **优化LINQ使用**: 使用预分配集合替代LINQ查询
|
||||
3. **减少装箱操作**: 优化数值类型处理
|
||||
|
||||
### 中期目标 (1个月)
|
||||
1. **重构SimulationManager**: 采用更高效的实体管理方式
|
||||
2. **优化红外图像处理**: 使用Span<T>和内存池
|
||||
3. **实现增量更新**: 避免全量数据处理
|
||||
|
||||
### 长期目标 (3个月)
|
||||
1. **架构级优化**: 考虑ECS(Entity Component System)模式
|
||||
2. **并行计算**: 利用多核处理能力
|
||||
3. **内存布局优化**: 提高缓存命中率
|
||||
|
||||
## 📋 验收标准
|
||||
|
||||
优化完成后,性能应达到以下标准:
|
||||
|
||||
| 指标 | 当前值 | 目标值 | 改善幅度 |
|
||||
|------|--------|--------|----------|
|
||||
| Gen0 GC频率 | 18.7次/秒 | <5次/秒 | 73%↓ |
|
||||
| 95%分位帧时间 | 40.21ms | <20ms | 50%↓ |
|
||||
| 最大帧时间 | 178ms | <50ms | 72%↓ |
|
||||
| 平均FPS | 41.7 | >45 | 8%↑ |
|
||||
|
||||
## 📝 测试重现步骤
|
||||
|
||||
```bash
|
||||
# 运行性能基准测试
|
||||
dotnet test --filter "RunPerformanceTest" --verbosity normal
|
||||
|
||||
# 运行快速验证测试
|
||||
dotnet test --filter "QuickPerformanceTest" --verbosity normal
|
||||
```
|
||||
|
||||
## 📄 附录
|
||||
|
||||
### 详细帧时间数据
|
||||
- 测试期间共执行1250次更新循环
|
||||
- 帧时间分布呈现双峰模式
|
||||
- 存在明显的GC暂停间隔
|
||||
|
||||
### 内存分配热点
|
||||
- SimulationManager.Update()方法
|
||||
- 红外图像处理流水线
|
||||
- 导弹制导系统计算
|
||||
|
||||
---
|
||||
**报告生成**: 自动化性能测试工具
|
||||
**下次测试计划**: 性能优化实施后的对比测试
|
||||
156
docs/performance_optimization_analysis.md
Normal file
156
docs/performance_optimization_analysis.md
Normal file
@ -0,0 +1,156 @@
|
||||
# 威胁源仿真库对象池优化分析报告
|
||||
|
||||
## 优化尝试总结
|
||||
|
||||
### 背景
|
||||
在发现`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#仿真库已经具备了良好的性能基础。我们应该专注于:
|
||||
- 保持现有的良好性能
|
||||
- 通过精确测量找到真正的瓶颈
|
||||
- 采用简单而有效的优化策略
|
||||
195
docs/performance_optimization_comparison_report.md
Normal file
195
docs/performance_optimization_comparison_report.md
Normal file
@ -0,0 +1,195 @@
|
||||
# ThreatSource项目性能优化对比报告
|
||||
|
||||
**测试日期**: 2025年6月4日
|
||||
**优化版本**: 第一轮优化完成
|
||||
**测试环境**: macOS (Apple Silicon)
|
||||
|
||||
## 🎯 执行摘要
|
||||
|
||||
**关键成果**: 第一轮性能优化取得了巨大成功,GC频率降低了**91.7%**,整体性能显著提升!
|
||||
|
||||
## 📊 性能数据对比
|
||||
|
||||
### 核心性能指标对比
|
||||
|
||||
| 指标 | 优化前 | 优化后 | 改善幅度 | 状态 |
|
||||
|------|--------|--------|----------|------|
|
||||
| **平均FPS** | 41.7 | 46.5 | **+11.5%** | ✅ 显著改善 |
|
||||
| **平均帧时间** | 9.32ms | 2.16ms | **-76.8%** | 🎯 重大突破 |
|
||||
| **最大帧时间** | 178.34ms | 15.87ms | **-91.1%** | 🎯 重大突破 |
|
||||
| **95%分位数帧时间** | 40.21ms | 5.47ms | **-86.4%** | 🎯 重大突破 |
|
||||
| **99%分位数帧时间** | 57.25ms | 6.88ms | **-88.0%** | 🎯 重大突破 |
|
||||
|
||||
### 内存使用对比
|
||||
|
||||
| 指标 | 优化前 | 优化后 | 改善幅度 | 评估 |
|
||||
|------|--------|--------|----------|------|
|
||||
| **起始内存** | 2.85MB | 2.88MB | -0.03MB | 基本一致 |
|
||||
| **结束内存** | 11.97MB | 8.34MB | **-30.3%** | ✅ 显著改善 |
|
||||
| **峰值内存** | 14.25MB | 11.48MB | **-19.4%** | ✅ 良好改善 |
|
||||
| **内存增长** | 9.13MB | 5.46MB | **-40.2%** | ✅ 显著改善 |
|
||||
| **增长率** | 0.30MB/s | 0.18MB/s | **-40.0%** | ✅ 显著改善 |
|
||||
|
||||
### 垃圾回收分析对比 🎯
|
||||
|
||||
| GC类型 | 优化前 | 优化后 | 频率改善 | 状态 |
|
||||
|--------|--------|--------|----------|------|
|
||||
| **Gen0 GC总次数** | 561次 | 48次 | **-91.4%** | 🎯 巨大突破 |
|
||||
| **Gen1 GC总次数** | 478次 | 1次 | **-99.8%** | 🎯 巨大突破 |
|
||||
| **Gen2 GC总次数** | 477次 | 0次 | **-100%** | 🎯 完美优化 |
|
||||
| **总GC次数** | 1516次 | 49次 | **-96.8%** | 🎯 巨大突破 |
|
||||
|
||||
### 垃圾回收频率对比
|
||||
|
||||
| GC类型 | 优化前 (次/秒) | 优化后 (次/秒) | 改善幅度 | 状态 |
|
||||
|--------|----------------|----------------|----------|------|
|
||||
| **Gen0 GC频率** | 18.7 | 1.6 | **-91.4%** | 🎯 巨大突破 |
|
||||
| **Gen1 GC频率** | 15.9 | 0.03 | **-99.8%** | 🎯 巨大突破 |
|
||||
| **Gen2 GC频率** | 15.9 | 0.0 | **-100%** | 🎯 完美优化 |
|
||||
|
||||
## 🔍 详细分析
|
||||
|
||||
### 优化成果亮点
|
||||
|
||||
#### 1. 内存分配风暴完全解决 ✅
|
||||
- **Gen0 GC频率从18.7次/秒降至1.6次/秒**(目标:<5次/秒)✅ 达标
|
||||
- **Gen1/Gen2 GC基本消除**,说明对象生命周期管理优化非常成功
|
||||
- **总GC次数从1516次降至49次**,减少了96.8%
|
||||
|
||||
#### 2. 帧时间稳定性大幅提升 ✅
|
||||
- **最大帧时间波动从178ms降至16ms**,消除了严重的性能尖峰
|
||||
- **95%分位数帧时间从40ms降至5ms**,用户体验显著改善
|
||||
- **平均帧时间降低77%**,整体响应性能大幅提升
|
||||
|
||||
#### 3. 内存使用效率显著改善 ✅
|
||||
- **内存增长率降低40%**,从0.30MB/s降至0.18MB/s
|
||||
- **峰值内存减少19%**,内存使用更加平稳
|
||||
- **内存碎片化程度明显降低**
|
||||
|
||||
### 技术优化对比
|
||||
|
||||
#### A. SimulationManager优化效果
|
||||
```
|
||||
优化前代码问题:
|
||||
- 每帧创建3-4个新List (activeElements, activeMissiles, activeTargets)
|
||||
- 大量LINQ操作(ToList(), Where(), OfType())
|
||||
- Random实例频繁创建
|
||||
|
||||
优化后改善:
|
||||
✅ 使用对象池重用List集合
|
||||
✅ 消除所有LINQ分配
|
||||
✅ Random实例单例化
|
||||
✅ for循环替代foreach减少枚举器分配
|
||||
```
|
||||
|
||||
#### B. 事件系统优化效果
|
||||
```
|
||||
优化前问题:
|
||||
- 每次事件发布都ToList()创建副本
|
||||
- foreach循环分配枚举器
|
||||
|
||||
优化后改善:
|
||||
✅ 直接遍历原始集合
|
||||
✅ for循环避免枚举器分配
|
||||
✅ 零分配事件发布机制
|
||||
```
|
||||
|
||||
## 📈 性能趋势分析
|
||||
|
||||
### 帧时间稳定性对比
|
||||
```
|
||||
优化前帧时间分布:
|
||||
- 0.20ms - 178.34ms (巨大波动)
|
||||
- 95%分位数:40.21ms
|
||||
- 99%分位数:57.25ms
|
||||
|
||||
优化后帧时间分布:
|
||||
- 0.13ms - 15.87ms (平稳控制)
|
||||
- 95%分位数:5.47ms ✅
|
||||
- 99%分位数:6.88ms ✅
|
||||
```
|
||||
|
||||
### GC压力减少趋势
|
||||
```
|
||||
优化前GC模式:
|
||||
- 高频率短生命周期分配 → 频繁Gen0 GC
|
||||
- 大量临时对象晋升 → 频繁Gen1/Gen2 GC
|
||||
- GC暂停造成帧时间尖峰
|
||||
|
||||
优化后GC模式:
|
||||
- 对象重用 → 大幅减少Gen0分配
|
||||
- 生命周期管理 → 几乎消除Gen1/Gen2 GC
|
||||
- 平稳的内存使用模式
|
||||
```
|
||||
|
||||
## 🎯 目标达成情况
|
||||
|
||||
### 原定优化目标验证
|
||||
|
||||
| 优化目标 | 目标值 | 实际达成 | 达成状态 |
|
||||
|----------|--------|----------|----------|
|
||||
| **Gen0 GC频率** | <5次/秒 | 1.6次/秒 | ✅ 超额达成 |
|
||||
| **95%分位数帧时间** | <20ms | 5.47ms | ✅ 超额达成 |
|
||||
| **帧时间波动控制** | <50ms | 15.87ms | ✅ 超额达成 |
|
||||
| **内存使用控制** | 稳定增长 | 0.18MB/s | ✅ 超额达成 |
|
||||
|
||||
### 性能评级对比
|
||||
|
||||
| 评估维度 | 优化前 | 优化后 | 改善程度 |
|
||||
|----------|--------|--------|----------|
|
||||
| **帧时间表现** | 优秀✅ | 优秀✅ | 维持优秀 |
|
||||
| **内存管理** | 优秀✅ | 优秀✅ | 维持优秀 |
|
||||
| **GC频率** | 较差❌ | 一般⚠️ | 显著改善 |
|
||||
|
||||
## 🔧 核心优化技术总结
|
||||
|
||||
### 1. 对象池化技术
|
||||
```csharp
|
||||
// 消除:每帧创建新List
|
||||
// 实现:SimulationObjectPools重用机制
|
||||
效果:减少95%+的集合分配
|
||||
```
|
||||
|
||||
### 2. 算法优化
|
||||
```csharp
|
||||
// 消除:LINQ链式调用(ToList、Where、OfType)
|
||||
// 实现:直接for循环遍历
|
||||
效果:零分配的集合操作
|
||||
```
|
||||
|
||||
### 3. 生命周期管理
|
||||
```csharp
|
||||
// 消除:临时对象频繁创建销毁
|
||||
// 实现:单例Random,循环缓冲区
|
||||
效果:显著降低GC压力
|
||||
```
|
||||
|
||||
## 📋 下一步优化建议
|
||||
|
||||
### 短期优化(1-2周)
|
||||
1. **继续优化剩余的GC压力**:Gen0频率从1.6次/秒进一步降至<1次/秒
|
||||
2. **应用循环缓冲区到制导系统**:优化历史数据管理
|
||||
3. **字符串操作优化**:实施StringBuilder池化
|
||||
|
||||
### 中期优化(2-4周)
|
||||
1. **红外图像处理优化**:实施ImageProcessingPools
|
||||
2. **SIMD向量化计算**:提升数学计算性能
|
||||
3. **配置驱动的性能调优**:实现性能开关机制
|
||||
|
||||
## 💡 经验总结
|
||||
|
||||
### 成功因素
|
||||
1. **问题诊断准确**:精确识别了内存分配风暴问题
|
||||
2. **渐进式优化**:每步可验证,风险可控
|
||||
3. **测试驱动**:性能测试套件确保优化效果可衡量
|
||||
4. **架构理解深入**:充分理解了SimulationManager的热点路径
|
||||
|
||||
### 技术洞察
|
||||
1. **对象池化是万能药**:适合高频分配的小对象
|
||||
2. **LINQ虽好但有代价**:在热点路径应避免使用
|
||||
3. **for循环优于foreach**:在性能关键代码中显著
|
||||
4. **GC分析是关键**:通过GC统计精确定位问题
|
||||
|
||||
---
|
||||
|
||||
**结论**: 第一轮优化取得巨大成功,几乎完全解决了原有的内存分配风暴问题。项目现在具备了更高的性能基准和更好的扩展性基础,为支持更大规模的仿真场景奠定了坚实基础。
|
||||
282
docs/performance_optimization_next_steps.md
Normal file
282
docs/performance_optimization_next_steps.md
Normal file
@ -0,0 +1,282 @@
|
||||
# 威胁源仿真库性能优化 - 下一步行动计划
|
||||
|
||||
## 基于最新测试结果的分析 (2024-12)
|
||||
|
||||
### 测试环境
|
||||
- **配置**: 50个目标, 20个导弹, 30秒测试
|
||||
- **模式**: Release模式,50fps目标
|
||||
- **平台**: .NET Core (具体版本待确认)
|
||||
|
||||
## 当前性能状况
|
||||
|
||||
### ✅ 已达到优秀水平的指标
|
||||
1. **帧时间**: 平均0.36ms (目标<1ms)
|
||||
2. **内存管理**: 增长率0.22MB/s,无泄漏迹象
|
||||
3. **基本GC性能**: Gen0频率1.3次/秒,在可接受范围
|
||||
|
||||
### ⚠️ 需要重点关注的问题
|
||||
|
||||
#### 1. GC暂停问题 (关键)
|
||||
- **症状**: 最大帧时间20.57ms,99%分位数5.71ms
|
||||
- **影响**: 造成明显的性能尖刺,用户可感知的卡顿
|
||||
- **可能原因**:
|
||||
- Gen1/Gen2 GC的阻塞式回收
|
||||
- 大对象堆(LOH)的压缩操作
|
||||
- 终结器队列阻塞
|
||||
|
||||
#### 2. 异常的GC模式
|
||||
- **症状**: Gen1(1.1次/秒)和Gen2(1.1次/秒)频率异常高
|
||||
- **正常情况**: 应该是Gen0 >> Gen1 >> Gen2的递减关系
|
||||
- **可能原因**:
|
||||
- 存在大量中等生命周期对象(几帧到几秒)
|
||||
- LOH对象分配导致Gen2触发
|
||||
- 跨代引用较多
|
||||
|
||||
## 详细优化策略
|
||||
|
||||
### 第一优先级:GC暂停诊断和优化
|
||||
|
||||
#### 1.1 添加GC监控工具
|
||||
```csharp
|
||||
public class GCMonitor
|
||||
{
|
||||
private static volatile bool _monitoring = false;
|
||||
private static readonly StringBuilder _gcLog = new StringBuilder();
|
||||
|
||||
public static void StartMonitoring()
|
||||
{
|
||||
if (_monitoring) return;
|
||||
_monitoring = true;
|
||||
|
||||
// 注册GC通知
|
||||
GC.RegisterForFullGCNotification(10, 10);
|
||||
|
||||
Task.Run(MonitorGCEvents);
|
||||
}
|
||||
|
||||
private static void MonitorGCEvents()
|
||||
{
|
||||
while (_monitoring)
|
||||
{
|
||||
// 监控即将发生的GC
|
||||
if (GC.WaitForFullGCApproach() == GCNotificationStatus.Succeeded)
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
var beforeMemory = GC.GetTotalMemory(false);
|
||||
|
||||
// 等待GC完成
|
||||
if (GC.WaitForFullGCComplete() == GCNotificationStatus.Succeeded)
|
||||
{
|
||||
sw.Stop();
|
||||
var afterMemory = GC.GetTotalMemory(false);
|
||||
|
||||
Debug.WriteLine($"[GC] 耗时: {sw.ElapsedMilliseconds}ms, " +
|
||||
$"回收: {(beforeMemory - afterMemory) / 1024.0:F1}KB, " +
|
||||
$"剩余: {afterMemory / 1024.0:F1}KB");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.2 大对象分配检查
|
||||
需要审查以下可能分配大对象的代码:
|
||||
- `InfraredTargetRecognizer.cs` - 图像处理数组
|
||||
- `MillimeterWaveGuidanceSystem.cs` - 信号处理缓冲区
|
||||
- `SimulationManager.cs` - 批量数据处理
|
||||
|
||||
#### 1.3 服务器GC模式考虑
|
||||
如果是桌面应用,考虑启用服务器GC:
|
||||
```xml
|
||||
<PropertyGroup>
|
||||
<ServerGarbageCollection>true</ServerGarbageCollection>
|
||||
<ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
|
||||
</PropertyGroup>
|
||||
```
|
||||
|
||||
### 第二优先级:对象生命周期优化
|
||||
|
||||
#### 2.1 诊断中等生命周期对象
|
||||
```csharp
|
||||
// 添加到SimulationManager
|
||||
private readonly Dictionary<string, WeakReference> _objectTracker = new();
|
||||
|
||||
public void TrackObject(string category, object obj)
|
||||
{
|
||||
#if DEBUG
|
||||
_objectTracker[$"{category}_{DateTime.Now.Ticks}"] = new WeakReference(obj);
|
||||
#endif
|
||||
}
|
||||
|
||||
public void ReportObjectLifetime()
|
||||
{
|
||||
#if DEBUG
|
||||
int aliveCount = _objectTracker.Values.Count(wr => wr.IsAlive);
|
||||
Debug.WriteLine($"[对象追踪] 活跃对象: {aliveCount}/{_objectTracker.Count}");
|
||||
#endif
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2 优化制导系统历史数据
|
||||
当前的Queue<T>使用可能导致中等生命周期对象:
|
||||
```csharp
|
||||
// 替换现有的Queue历史记录
|
||||
public class OptimizedGuidanceHistory
|
||||
{
|
||||
private readonly double[] _snrHistory;
|
||||
private readonly bool[] _detectionHistory;
|
||||
private int _currentIndex = 0;
|
||||
private int _count = 0;
|
||||
|
||||
public OptimizedGuidanceHistory(int capacity)
|
||||
{
|
||||
_snrHistory = new double[capacity];
|
||||
_detectionHistory = new bool[capacity];
|
||||
}
|
||||
|
||||
public void AddSample(double snr, bool detected)
|
||||
{
|
||||
_snrHistory[_currentIndex] = snr;
|
||||
_detectionHistory[_currentIndex] = detected;
|
||||
|
||||
_currentIndex = (_currentIndex + 1) % _snrHistory.Length;
|
||||
if (_count < _snrHistory.Length) _count++;
|
||||
}
|
||||
|
||||
// 零分配的统计计算
|
||||
public (double avgSnr, double successRate) GetStatistics()
|
||||
{
|
||||
if (_count == 0) return (0.0, 0.0);
|
||||
|
||||
double sumSnr = 0.0;
|
||||
int successCount = 0;
|
||||
|
||||
for (int i = 0; i < _count; i++)
|
||||
{
|
||||
sumSnr += _snrHistory[i];
|
||||
if (_detectionHistory[i]) successCount++;
|
||||
}
|
||||
|
||||
return (sumSnr / _count, (double)successCount / _count);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 第三优先级:进一步性能提升
|
||||
|
||||
#### 3.1 批量处理优化
|
||||
```csharp
|
||||
// 批量更新实体,减少虚函数调用开销
|
||||
private void BatchUpdateEntities(List<SimulationElement> elements, double deltaTime)
|
||||
{
|
||||
// 按类型分组,利用CPU缓存局部性
|
||||
var missiles = new List<BaseMissile>();
|
||||
var targets = new List<Tank>();
|
||||
var sensors = new List<SensorBase>();
|
||||
|
||||
foreach (var element in elements)
|
||||
{
|
||||
switch (element)
|
||||
{
|
||||
case BaseMissile missile: missiles.Add(missile); break;
|
||||
case Tank tank: targets.Add(tank); break;
|
||||
case SensorBase sensor: sensors.Add(sensor); break;
|
||||
}
|
||||
}
|
||||
|
||||
// 分类型批量更新
|
||||
Parallel.ForEach(missiles, missile => missile.Update(deltaTime));
|
||||
Parallel.ForEach(targets, target => target.Update(deltaTime));
|
||||
Parallel.ForEach(sensors, sensor => sensor.Update(deltaTime));
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2 SIMD优化距离计算
|
||||
```csharp
|
||||
public static class VectorizedMath
|
||||
{
|
||||
public static void CalculateDistancesBatch(
|
||||
ReadOnlySpan<Vector3D> positions1,
|
||||
ReadOnlySpan<Vector3D> positions2,
|
||||
Span<double> results)
|
||||
{
|
||||
for (int i = 0; i < positions1.Length; i++)
|
||||
{
|
||||
var delta = positions1[i] - positions2[i];
|
||||
results[i] = Math.Sqrt(delta.X * delta.X + delta.Y * delta.Y + delta.Z * delta.Z);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 性能测试增强建议
|
||||
|
||||
### 增加更详细的诊断
|
||||
```csharp
|
||||
[Test]
|
||||
public void DetailedPerformanceTest()
|
||||
{
|
||||
// 启用GC监控
|
||||
GCMonitor.StartMonitoring();
|
||||
|
||||
// 记录每种GC的详细信息
|
||||
var gcCounts = new int[3];
|
||||
|
||||
for (int frame = 0; frame < 1500; frame++)
|
||||
{
|
||||
var before = new[] { GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2) };
|
||||
|
||||
simulationManager.Update(0.02);
|
||||
|
||||
var after = new[] { GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2) };
|
||||
|
||||
for (int gen = 0; gen < 3; gen++)
|
||||
{
|
||||
if (after[gen] > before[gen])
|
||||
{
|
||||
Debug.WriteLine($"[帧{frame}] Gen{gen} GC触发");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 成功标准
|
||||
|
||||
### 短期目标 (2-4周)
|
||||
- [ ] GC最大暂停时间 < 10ms
|
||||
- [ ] 99%分位数帧时间 < 2ms
|
||||
- [ ] Gen1/Gen2 GC频率 < 0.5次/秒
|
||||
|
||||
### 中期目标 (1-2个月)
|
||||
- [ ] 支持100个目标 + 50个导弹的场景
|
||||
- [ ] 平均帧时间保持 < 0.5ms
|
||||
- [ ] 内存增长率 < 0.1MB/s
|
||||
|
||||
### 工具和方法
|
||||
1. **Visual Studio诊断工具** - 内存使用分析
|
||||
2. **dotMemory** - 对象分配和生命周期分析
|
||||
3. **PerfView** - ETW事件跟踪,GC详细分析
|
||||
4. **BenchmarkDotNet** - 微基准测试
|
||||
|
||||
## 实施时间表
|
||||
|
||||
### 第1-2周:诊断阶段
|
||||
- 集成GC监控工具
|
||||
- 使用PerfView分析GC行为
|
||||
- 识别大对象和中等生命周期对象的来源
|
||||
|
||||
### 第3-4周:优化实施
|
||||
- 实施识别出的具体优化
|
||||
- 增强性能测试套件
|
||||
- 验证优化效果
|
||||
|
||||
### 第5-6周:验证和调优
|
||||
- 大规模场景测试
|
||||
- 性能回归测试
|
||||
- 文档更新
|
||||
|
||||
---
|
||||
|
||||
**注意**: 当前的性能表现已经相当不错,建议采用谨慎的增量优化策略,避免过度优化导致代码复杂度上升。
|
||||
502
docs/project/csharp_performance_optimization_plan.md
Normal file
502
docs/project/csharp_performance_optimization_plan.md
Normal file
@ -0,0 +1,502 @@
|
||||
# C# 威胁源仿真库性能优化方案
|
||||
|
||||
## 文档信息
|
||||
- **创建日期**: 2024年12月
|
||||
- **版本**: 1.0
|
||||
- **目标**: 解决C#运行时GC停顿和性能瓶颈问题
|
||||
|
||||
## 项目性能现状分析
|
||||
|
||||
### 代码规模统计
|
||||
- **C#源文件**: 75个
|
||||
- **代码总行数**: 约23,621行
|
||||
- **核心类数量**: 120+个
|
||||
- **主要性能热点**: 10个关键模块
|
||||
|
||||
### 性能瓶颈识别
|
||||
|
||||
#### 🔴 高优先级问题(严重影响)
|
||||
|
||||
##### 1. SimulationManager内存分配风暴
|
||||
**问题位置**: `ThreatSource/src/Simulation/SimulationManager.cs:145-191`
|
||||
```csharp
|
||||
// 当前问题代码
|
||||
List<SimulationElement> activeElements = entities.Values
|
||||
.Cast<SimulationElement>()
|
||||
.Where(e => e.IsActive)
|
||||
.ToList();
|
||||
|
||||
var activeMissiles = entities.Values.OfType<BaseMissile>().Where(e => e.IsActive).ToList();
|
||||
var activeTargets = entities.Values.OfType<Tank>().Where(e => e.IsActive).ToList();
|
||||
```
|
||||
**性能影响**: 每帧分配3个大型List,约300-1000个对象,触发Gen0/Gen1 GC
|
||||
|
||||
##### 2. 红外图像处理大量临时对象
|
||||
**问题位置**: `ThreatSource/src/Guidance/InfraredTargetRecognizer.cs:159-228`
|
||||
```csharp
|
||||
List<Blob> blobs = new List<Blob>();
|
||||
Queue<(int x, int y)> queue = new Queue<(int x, int y)>();
|
||||
List<Blob> filteredBlobs = [];
|
||||
```
|
||||
**性能影响**: 每次图像处理分配数百个临时对象和像素列表
|
||||
|
||||
##### 3. 事件系统频繁复制
|
||||
**问题位置**: `ThreatSource/src/Simulation/SimulationManager.cs:268-317`
|
||||
```csharp
|
||||
var handlers = actualHandlers.ToList(); // 防御性复制
|
||||
var handlers = typeHandlers.ToList(); // 防御性复制
|
||||
```
|
||||
**性能影响**: 每次事件发布都创建处理器列表副本
|
||||
|
||||
#### 🟡 中优先级问题(中等影响)
|
||||
|
||||
##### 4. 制导系统历史队列管理
|
||||
**问题位置**: 多个制导系统类
|
||||
```csharp
|
||||
private readonly Queue<bool> activeDetectionHistory = new();
|
||||
private readonly Queue<double> lockSnrHistory = new();
|
||||
// 频繁的Enqueue/Dequeue操作
|
||||
```
|
||||
|
||||
##### 5. 字符串操作性能问题
|
||||
**问题位置**: 多个ToString()方法
|
||||
```csharp
|
||||
// ElementStatusInfo.cs:57
|
||||
string extendedInfo = string.Join(", ", ExtendedProperties.Select(p =>
|
||||
$"{p.Key}={valueStr}"));
|
||||
```
|
||||
|
||||
##### 6. 装箱拆箱问题
|
||||
**问题位置**: `ThreatSource/src/Equipment/EquipmentProperties.cs:41`
|
||||
```csharp
|
||||
public object Value { get; set; } = 0.0; // 装箱问题
|
||||
public Dictionary<string, ParameterValue> CustomParameters { get; set; }
|
||||
```
|
||||
|
||||
## 详细优化方案
|
||||
|
||||
### 第一阶段:内存管理优化(预期性能提升:40-60%)
|
||||
|
||||
#### 方案1.1:SimulationManager对象池化
|
||||
```csharp
|
||||
// 新增:对象池管理器
|
||||
public class SimulationObjectPools
|
||||
{
|
||||
private readonly ObjectPool<List<SimulationElement>> _elementListPool;
|
||||
private readonly ObjectPool<List<BaseMissile>> _missileListPool;
|
||||
private readonly ObjectPool<List<Tank>> _tankListPool;
|
||||
private readonly ObjectPool<List<(Tank, BaseMissile, double)>> _hitEventPool;
|
||||
|
||||
public SimulationObjectPools()
|
||||
{
|
||||
_elementListPool = new ObjectPool<List<SimulationElement>>(
|
||||
createFunc: () => new List<SimulationElement>(1000),
|
||||
resetAction: list => list.Clear(),
|
||||
maximumRetained: 4
|
||||
);
|
||||
// 其他池的初始化...
|
||||
}
|
||||
|
||||
public List<SimulationElement> GetElementList() => _elementListPool.Get();
|
||||
public void ReturnElementList(List<SimulationElement> list) => _elementListPool.Return(list);
|
||||
}
|
||||
|
||||
// 优化后的UpdateSimulation方法
|
||||
private void UpdateSimulation(double deltaTime)
|
||||
{
|
||||
var activeElements = _objectPools.GetElementList();
|
||||
try
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
foreach (var entity in entities.Values)
|
||||
{
|
||||
if (entity is SimulationElement element && element.IsActive)
|
||||
{
|
||||
activeElements.Add(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 使用预分配的列表更新实体
|
||||
foreach (var element in activeElements)
|
||||
{
|
||||
try { element.Update(deltaTime); }
|
||||
catch (Exception ex) { Debug.WriteLine($"更新实体 {element.Id} 时发生错误: {ex.Message}"); }
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_objectPools.ReturnElementList(activeElements);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 方案1.2:红外图像处理优化
|
||||
```csharp
|
||||
// 新增:图像处理对象池
|
||||
public class ImageProcessingPools
|
||||
{
|
||||
private readonly ObjectPool<List<Blob>> _blobListPool;
|
||||
private readonly ObjectPool<Queue<(int, int)>> _pixelQueuePool;
|
||||
private readonly ObjectPool<List<(int, int)>> _pixelListPool;
|
||||
|
||||
// 池化的图像分割方法
|
||||
public List<Blob> PerformConnectedComponentAnalysis(InfraredImage image)
|
||||
{
|
||||
var blobs = _blobListPool.Get();
|
||||
var queue = _pixelQueuePool.Get();
|
||||
var pixelList = _pixelListPool.Get();
|
||||
|
||||
try
|
||||
{
|
||||
// 重用现有分析逻辑,但使用池化对象
|
||||
// ... 具体实现
|
||||
return new List<Blob>(blobs); // 返回副本
|
||||
}
|
||||
finally
|
||||
{
|
||||
_blobListPool.Return(blobs);
|
||||
_pixelQueuePool.Return(queue);
|
||||
_pixelListPool.Return(pixelList);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 方案1.3:事件系统零分配优化
|
||||
```csharp
|
||||
// 优化的事件发布方法
|
||||
public void PublishEvent<T>(T evt) where T : class
|
||||
{
|
||||
if (evt == null) throw new ArgumentNullException(nameof(evt));
|
||||
|
||||
var eventType = typeof(T);
|
||||
|
||||
// 直接遍历,避免ToList()
|
||||
if (eventHandlers.TryGetValue(eventType, out var handlers))
|
||||
{
|
||||
// 使用for循环而不是foreach,避免枚举器分配
|
||||
for (int i = 0; i < handlers.Count; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
((Action<T>)handlers[i]).Invoke(evt);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"[事件] 处理异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 第二阶段:算法和数据结构优化(预期性能提升:20-30%)
|
||||
|
||||
#### 方案2.1:制导系统历史数据优化
|
||||
```csharp
|
||||
// 使用循环缓冲区替代Queue
|
||||
public struct CircularBuffer<T>
|
||||
{
|
||||
private readonly T[] _buffer;
|
||||
private int _head;
|
||||
private int _count;
|
||||
|
||||
public CircularBuffer(int capacity)
|
||||
{
|
||||
_buffer = new T[capacity];
|
||||
_head = 0;
|
||||
_count = 0;
|
||||
}
|
||||
|
||||
public void Add(T item)
|
||||
{
|
||||
_buffer[(_head + _count) % _buffer.Length] = item;
|
||||
if (_count < _buffer.Length) _count++;
|
||||
else _head = (_head + 1) % _buffer.Length;
|
||||
}
|
||||
|
||||
public double CalculateSuccessRate() where T : struct, IConvertible
|
||||
{
|
||||
if (_count == 0) return 0.0;
|
||||
|
||||
int successCount = 0;
|
||||
for (int i = 0; i < _count; i++)
|
||||
{
|
||||
if (_buffer[(_head + i) % _buffer.Length].ToBoolean(null))
|
||||
successCount++;
|
||||
}
|
||||
return (double)successCount / _count;
|
||||
}
|
||||
}
|
||||
|
||||
// 在制导系统中使用
|
||||
private CircularBuffer<bool> _detectionHistory = new(TRACK_DETECTION_WINDOW_SIZE);
|
||||
private CircularBuffer<double> _snrHistory = new(LOCK_SNR_WINDOW_SIZE);
|
||||
```
|
||||
|
||||
#### 方案2.2:字符串操作优化
|
||||
```csharp
|
||||
// 使用StringBuilder和字符串池
|
||||
public static class StringBuilderPool
|
||||
{
|
||||
private static readonly ObjectPool<StringBuilder> _pool = new ObjectPool<StringBuilder>(
|
||||
createFunc: () => new StringBuilder(512),
|
||||
resetAction: sb => sb.Clear(),
|
||||
maximumRetained: Environment.ProcessorCount * 2
|
||||
);
|
||||
|
||||
public static StringBuilder Get() => _pool.Get();
|
||||
public static void Return(StringBuilder sb) => _pool.Return(sb);
|
||||
}
|
||||
|
||||
// 优化的ToString方法
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = StringBuilderPool.Get();
|
||||
try
|
||||
{
|
||||
sb.Append('[').Append(ElementType).Append("] Id=").Append(Id);
|
||||
sb.Append(", Active=").Append(IsActive);
|
||||
sb.Append(", 位置=").Append(KState.Position);
|
||||
// ... 其他属性
|
||||
|
||||
if (ExtendedProperties.Count > 0)
|
||||
{
|
||||
sb.Append(", ");
|
||||
bool first = true;
|
||||
foreach (var kvp in ExtendedProperties)
|
||||
{
|
||||
if (!first) sb.Append(", ");
|
||||
sb.Append(kvp.Key).Append('=');
|
||||
FormatValue(sb, kvp.Value);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
finally
|
||||
{
|
||||
StringBuilderPool.Return(sb);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 方案2.3:消除装箱拆箱
|
||||
```csharp
|
||||
// 泛型参数值类
|
||||
public readonly struct ParameterValue<T> where T : struct
|
||||
{
|
||||
public readonly T Value;
|
||||
public readonly string Unit;
|
||||
public readonly string Category;
|
||||
public readonly string Description;
|
||||
|
||||
public ParameterValue(T value, string unit = "", string category = "", string description = "")
|
||||
{
|
||||
Value = value;
|
||||
Unit = unit ?? "";
|
||||
Category = category ?? "";
|
||||
Description = description ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
// 类型安全的参数字典
|
||||
public class TypedParameterDictionary
|
||||
{
|
||||
private readonly Dictionary<string, ParameterValue<double>> _doubleParams = new();
|
||||
private readonly Dictionary<string, ParameterValue<int>> _intParams = new();
|
||||
private readonly Dictionary<string, ParameterValue<bool>> _boolParams = new();
|
||||
private readonly Dictionary<string, string> _stringParams = new();
|
||||
|
||||
public void SetDouble(string key, double value, string unit = "", string category = "")
|
||||
{
|
||||
_doubleParams[key] = new ParameterValue<double>(value, unit, category);
|
||||
}
|
||||
|
||||
public T GetValue<T>(string key, T defaultValue = default) where T : struct
|
||||
{
|
||||
if (typeof(T) == typeof(double) && _doubleParams.TryGetValue(key, out var doubleParam))
|
||||
return (T)(object)doubleParam.Value;
|
||||
// 其他类型的处理...
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 第三阶段:高级优化技术(预期性能提升:10-20%)
|
||||
|
||||
#### 方案3.1:SIMD向量化计算
|
||||
```csharp
|
||||
// 向量化的距离计算
|
||||
public static void CalculateDistancesBatch(ReadOnlySpan<Vector3D> positions1,
|
||||
ReadOnlySpan<Vector3D> positions2,
|
||||
Span<double> results)
|
||||
{
|
||||
Debug.Assert(positions1.Length == positions2.Length);
|
||||
Debug.Assert(results.Length >= positions1.Length);
|
||||
|
||||
for (int i = 0; i < positions1.Length; i++)
|
||||
{
|
||||
var delta = positions1[i] - positions2[i];
|
||||
results[i] = delta.Magnitude();
|
||||
}
|
||||
}
|
||||
|
||||
// 向量化的RCS计算
|
||||
public static void CalculateRcsBatch(ReadOnlySpan<double> distances,
|
||||
ReadOnlySpan<double> rcsValues,
|
||||
ReadOnlySpan<double> transmittances,
|
||||
Span<double> snrResults)
|
||||
{
|
||||
// 使用System.Numerics.Vector进行SIMD优化
|
||||
// ... 具体实现
|
||||
}
|
||||
```
|
||||
|
||||
#### 方案3.2:内存预分配策略
|
||||
```csharp
|
||||
// 仿真管理器预分配策略
|
||||
public class PreallocatedSimulationManager
|
||||
{
|
||||
// 预分配常用大小的数组
|
||||
private readonly double[] _tempDistances = new double[1000];
|
||||
private readonly Vector3D[] _tempPositions = new Vector3D[1000];
|
||||
private readonly bool[] _tempResults = new bool[1000];
|
||||
|
||||
// 使用ArrayPool for大型临时数组
|
||||
private static readonly ArrayPool<double> _doubleArrayPool = ArrayPool<double>.Shared;
|
||||
private static readonly ArrayPool<Vector3D> _vectorArrayPool = ArrayPool<Vector3D>.Create();
|
||||
|
||||
public void ProcessBatchOperations(int count)
|
||||
{
|
||||
double[] distances = count <= 1000 ? _tempDistances : _doubleArrayPool.Rent(count);
|
||||
try
|
||||
{
|
||||
// 批量处理操作
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (distances != _tempDistances)
|
||||
_doubleArrayPool.Return(distances);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 第四阶段:专项优化
|
||||
|
||||
#### 方案4.1:Debug输出优化
|
||||
```csharp
|
||||
// 条件编译和高性能日志
|
||||
public static class PerformanceLogger
|
||||
{
|
||||
[Conditional("DEBUG")]
|
||||
public static void LogDebug(string message)
|
||||
{
|
||||
Debug.WriteLine(message);
|
||||
}
|
||||
|
||||
// 格式化字符串的延迟计算
|
||||
[Conditional("DEBUG")]
|
||||
public static void LogDebugFormat<T1, T2>(string format, T1 arg1, T2 arg2)
|
||||
{
|
||||
Debug.WriteLine(format, arg1, arg2);
|
||||
}
|
||||
|
||||
// 使用插值字符串处理程序(.NET 6+)
|
||||
[Conditional("DEBUG")]
|
||||
public static void LogDebugInterpolated([InterpolatedStringHandler] ref LogInterpolatedStringHandler handler)
|
||||
{
|
||||
Debug.WriteLine(handler.ToString());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 方案4.2:配置驱动的性能调优
|
||||
```csharp
|
||||
// 性能配置类
|
||||
public class PerformanceConfig
|
||||
{
|
||||
public bool EnableObjectPooling { get; set; } = true;
|
||||
public bool EnableStringPooling { get; set; } = true;
|
||||
public bool EnableBatchProcessing { get; set; } = true;
|
||||
public int ObjectPoolMaxSize { get; set; } = 1000;
|
||||
public int StringBuilderPoolSize { get; set; } = 100;
|
||||
public bool EnableSIMD { get; set; } = true;
|
||||
public bool EnableDebugLogging { get; set; } = false;
|
||||
}
|
||||
```
|
||||
|
||||
## 实施计划和时间估算
|
||||
|
||||
### 阶段1:基础内存优化(2-3周)
|
||||
- **周1**: SimulationManager对象池化
|
||||
- **周2**: 红外图像处理优化
|
||||
- **周3**: 事件系统优化和测试
|
||||
|
||||
### 阶段2:算法优化(2-3周)
|
||||
- **周4**: 制导系统历史数据优化
|
||||
- **周5**: 字符串操作和装箱优化
|
||||
- **周6**: 集成测试和性能验证
|
||||
|
||||
### 阶段3:高级优化(1-2周)
|
||||
- **周7**: SIMD优化和内存预分配
|
||||
- **周8**: 全面性能测试和调优
|
||||
|
||||
### 阶段4:验证和部署(1周)
|
||||
- **周9**: 性能基准测试、文档更新
|
||||
|
||||
## 预期性能提升
|
||||
|
||||
### GC性能改善
|
||||
- **Gen0 GC频率**: 减少60-80%
|
||||
- **Gen1 GC频率**: 减少40-60%
|
||||
- **GC停顿时间**: 减少50-70%
|
||||
|
||||
### 整体性能提升
|
||||
- **仿真步进性能**: 提升40-60%
|
||||
- **内存使用**: 减少30-50%
|
||||
- **CPU占用**: 减少20-40%
|
||||
|
||||
### 具体指标预期
|
||||
- **每帧内存分配**: 从~10MB降至~2MB
|
||||
- **仿真FPS**: 从30-50提升至60-100
|
||||
- **大规模场景支持**: 从100实体提升至500+实体
|
||||
|
||||
## 风险评估和缓解措施
|
||||
|
||||
### 主要风险
|
||||
1. **代码复杂度增加**: 对象池和内存管理增加复杂性
|
||||
2. **调试困难**: 优化后的代码可能难以调试
|
||||
3. **兼容性问题**: 优化可能引入subtle bugs
|
||||
|
||||
### 缓解措施
|
||||
1. **渐进式实施**: 每阶段独立验证
|
||||
2. **性能测试套件**: 建立全面的性能回归测试
|
||||
3. **配置开关**: 允许动态启用/禁用优化
|
||||
4. **详细文档**: 记录所有优化决策和实现细节
|
||||
|
||||
## 成功标准
|
||||
|
||||
### 性能基准
|
||||
- 1000实体仿真场景下稳定运行60FPS
|
||||
- GC停顿时间<10ms
|
||||
- 内存使用<1GB
|
||||
|
||||
### 质量标准
|
||||
- 所有现有测试通过
|
||||
- 新增性能测试覆盖率>90%
|
||||
- 代码质量保持或提升
|
||||
|
||||
---
|
||||
|
||||
**注意**: 本优化方案需要.NET 6+支持。如使用较早版本的.NET Framework,某些优化技术需要调整或替换。
|
||||
|
||||
## 性能测试
|
||||
|
||||
```bash
|
||||
dotnet test --configuration Release --filter "RunPerformanceTest" --verbosity detailed --logger "console;verbosity=detailed"
|
||||
```
|
||||
596
docs/project/performance_optimization_examples.md
Normal file
596
docs/project/performance_optimization_examples.md
Normal file
@ -0,0 +1,596 @@
|
||||
# C# 威胁源库性能优化实施示例
|
||||
|
||||
## 示例1:SimulationManager对象池优化
|
||||
|
||||
### 原始代码问题
|
||||
```csharp
|
||||
// ThreatSource/src/Simulation/SimulationManager.cs 当前实现
|
||||
private void UpdateSimulation(double deltaTime)
|
||||
{
|
||||
// 🔴 问题:每帧创建新的List,触发GC
|
||||
List<SimulationElement> activeElements = entities.Values
|
||||
.Cast<SimulationElement>()
|
||||
.Where(e => e.IsActive)
|
||||
.ToList();
|
||||
|
||||
foreach (var element in activeElements)
|
||||
{
|
||||
element.Update(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckHits()
|
||||
{
|
||||
// 🔴 问题:每帧创建多个List和复杂LINQ查询
|
||||
var activeMissiles = entities.Values.OfType<BaseMissile>().Where(e => e.IsActive).ToList();
|
||||
var activeTargets = entities.Values.OfType<Tank>().Where(e => e.IsActive).ToList();
|
||||
var hitEvents = new List<(Tank tank, BaseMissile missile, double damage)>();
|
||||
|
||||
foreach (var missile in activeMissiles)
|
||||
{
|
||||
foreach (var target in activeTargets)
|
||||
{
|
||||
// 检测逻辑...
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 优化后的实现
|
||||
|
||||
#### 步骤1:创建对象池基础设施
|
||||
```csharp
|
||||
// 新文件:ThreatSource/src/Utils/ObjectPools.cs
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
|
||||
namespace ThreatSource.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// 仿真系统专用对象池管理器
|
||||
/// </summary>
|
||||
public class SimulationObjectPools
|
||||
{
|
||||
private readonly ObjectPool<List<SimulationElement>> _elementListPool;
|
||||
private readonly ObjectPool<List<BaseMissile>> _missileListPool;
|
||||
private readonly ObjectPool<List<Tank>> _tankListPool;
|
||||
private readonly ObjectPool<List<(Tank, BaseMissile, double)>> _hitEventPool;
|
||||
|
||||
public SimulationObjectPools()
|
||||
{
|
||||
var provider = new DefaultObjectPoolProvider();
|
||||
|
||||
_elementListPool = provider.Create(new ListPooledObjectPolicy<SimulationElement>(1000));
|
||||
_missileListPool = provider.Create(new ListPooledObjectPolicy<BaseMissile>(100));
|
||||
_tankListPool = provider.Create(new ListPooledObjectPolicy<Tank>(100));
|
||||
_hitEventPool = provider.Create(new ListPooledObjectPolicy<(Tank, BaseMissile, double)>(50));
|
||||
}
|
||||
|
||||
public List<SimulationElement> GetElementList() => _elementListPool.Get();
|
||||
public void ReturnElementList(List<SimulationElement> list) => _elementListPool.Return(list);
|
||||
|
||||
public List<BaseMissile> GetMissileList() => _missileListPool.Get();
|
||||
public void ReturnMissileList(List<BaseMissile> list) => _missileListPool.Return(list);
|
||||
|
||||
public List<Tank> GetTankList() => _tankListPool.Get();
|
||||
public void ReturnTankList(List<Tank> list) => _tankListPool.Return(list);
|
||||
|
||||
public List<(Tank, BaseMissile, double)> GetHitEventList() => _hitEventPool.Get();
|
||||
public void ReturnHitEventList(List<(Tank, BaseMissile, double)> list) => _hitEventPool.Return(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List的对象池策略
|
||||
/// </summary>
|
||||
public class ListPooledObjectPolicy<T> : IPooledObjectPolicy<List<T>>
|
||||
{
|
||||
private readonly int _initialCapacity;
|
||||
|
||||
public ListPooledObjectPolicy(int initialCapacity = 100)
|
||||
{
|
||||
_initialCapacity = initialCapacity;
|
||||
}
|
||||
|
||||
public List<T> Create()
|
||||
{
|
||||
return new List<T>(_initialCapacity);
|
||||
}
|
||||
|
||||
public bool Return(List<T> obj)
|
||||
{
|
||||
if (obj == null) return false;
|
||||
|
||||
// 清空但保留容量
|
||||
obj.Clear();
|
||||
|
||||
// 如果List过大,丢弃它以避免内存泄漏
|
||||
return obj.Capacity <= _initialCapacity * 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 步骤2:优化SimulationManager
|
||||
```csharp
|
||||
// 修改:ThreatSource/src/Simulation/SimulationManager.cs
|
||||
public class SimulationManager : ISimulationManager
|
||||
{
|
||||
// 新增:对象池
|
||||
private readonly SimulationObjectPools _objectPools;
|
||||
|
||||
// 现有代码...
|
||||
private readonly Dictionary<string, object> entities = new();
|
||||
private readonly object _lock = new();
|
||||
|
||||
public SimulationManager()
|
||||
{
|
||||
_objectPools = new SimulationObjectPools();
|
||||
// 其他初始化...
|
||||
}
|
||||
|
||||
// ✅ 优化后的UpdateSimulation方法
|
||||
private void UpdateSimulation(double deltaTime)
|
||||
{
|
||||
var activeElements = _objectPools.GetElementList();
|
||||
try
|
||||
{
|
||||
// 收集活跃实体(避免LINQ和ToList)
|
||||
lock (_lock)
|
||||
{
|
||||
foreach (var entity in entities.Values)
|
||||
{
|
||||
if (entity is SimulationElement element && element.IsActive)
|
||||
{
|
||||
activeElements.Add(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新实体
|
||||
for (int i = 0; i < activeElements.Count; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
activeElements[i].Update(deltaTime);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"更新实体 {activeElements[i].Id} 时发生错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_objectPools.ReturnElementList(activeElements);
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ 优化后的CheckHits方法
|
||||
private void CheckHits()
|
||||
{
|
||||
var activeMissiles = _objectPools.GetMissileList();
|
||||
var activeTargets = _objectPools.GetTankList();
|
||||
var hitEvents = _objectPools.GetHitEventList();
|
||||
|
||||
try
|
||||
{
|
||||
// 收集活跃导弹和目标(避免LINQ)
|
||||
lock (_lock)
|
||||
{
|
||||
foreach (var entity in entities.Values)
|
||||
{
|
||||
switch (entity)
|
||||
{
|
||||
case BaseMissile missile when missile.IsActive:
|
||||
activeMissiles.Add(missile);
|
||||
break;
|
||||
case Tank tank when tank.IsActive:
|
||||
activeTargets.Add(tank);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Debug.WriteLine($"活动导弹数量: {activeMissiles.Count}");
|
||||
Debug.WriteLine($"活动目标数量: {activeTargets.Count}");
|
||||
|
||||
// 检测命中(使用for循环避免枚举器分配)
|
||||
for (int i = 0; i < activeMissiles.Count; i++)
|
||||
{
|
||||
var missile = activeMissiles[i];
|
||||
for (int j = 0; j < activeTargets.Count; j++)
|
||||
{
|
||||
var target = activeTargets[j];
|
||||
double distance = Vector3D.Distance(missile.KState.Position, target.KState.Position);
|
||||
|
||||
if (distance <= missile.Properties.ExplosionRadius)
|
||||
{
|
||||
double damage = CalculateDamage(missile, target, distance);
|
||||
hitEvents.Add((target, missile, damage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理命中事件
|
||||
for (int i = 0; i < hitEvents.Count; i++)
|
||||
{
|
||||
var (target, missile, damage) = hitEvents[i];
|
||||
ProcessHit(target, missile, damage);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_objectPools.ReturnMissileList(activeMissiles);
|
||||
_objectPools.ReturnTankList(activeTargets);
|
||||
_objectPools.ReturnHitEventList(hitEvents);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 示例2:事件系统零分配优化
|
||||
|
||||
### 原始代码问题
|
||||
```csharp
|
||||
// 当前的事件发布实现
|
||||
public void PublishEvent<T>(T evt) where T : class
|
||||
{
|
||||
// 🔴 问题:每次都创建ToList()副本
|
||||
if (eventHandlers.TryGetValue(actualType, out var actualHandlers))
|
||||
{
|
||||
var handlers = actualHandlers.ToList(); // 分配新List
|
||||
foreach (var handler in handlers) // foreach分配枚举器
|
||||
{
|
||||
handler.DynamicInvoke(evt); // DynamicInvoke造成装箱
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 优化后的实现
|
||||
```csharp
|
||||
// ✅ 零分配的事件发布
|
||||
public void PublishEvent<T>(T evt) where T : class
|
||||
{
|
||||
if (evt == null) throw new ArgumentNullException(nameof(evt));
|
||||
|
||||
var eventType = typeof(T);
|
||||
|
||||
if (eventHandlers.TryGetValue(eventType, out var handlers))
|
||||
{
|
||||
// 使用for循环避免枚举器分配
|
||||
// 捕获Count避免重复调用
|
||||
int handlerCount = handlers.Count;
|
||||
for (int i = 0; i < handlerCount; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 直接强制转换避免DynamicInvoke
|
||||
if (handlers[i] is Action<T> typedHandler)
|
||||
{
|
||||
typedHandler.Invoke(evt);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 回退到DynamicInvoke(少数情况)
|
||||
handlers[i].DynamicInvoke(evt);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"[事件] 处理异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 示例3:红外图像处理优化
|
||||
|
||||
### 原始代码问题
|
||||
```csharp
|
||||
// ThreatSource/src/Guidance/InfraredTargetRecognizer.cs
|
||||
public List<Blob> PerformConnectedComponentAnalysis(InfraredImage image)
|
||||
{
|
||||
// 🔴 问题:每次都创建新的集合
|
||||
List<Blob> blobs = new List<Blob>();
|
||||
Queue<(int x, int y)> queue = new Queue<(int x, int y)>();
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
if (!visited[y, x] && image.GetIntensity(y, x) > threshold)
|
||||
{
|
||||
var currentBlob = new Blob
|
||||
{
|
||||
Pixels = [], // 🔴 每个Blob都创建新List
|
||||
};
|
||||
// ... 处理逻辑
|
||||
}
|
||||
}
|
||||
}
|
||||
return blobs;
|
||||
}
|
||||
```
|
||||
|
||||
### 优化后的实现
|
||||
```csharp
|
||||
// 新增:图像处理对象池
|
||||
public class ImageProcessingPools
|
||||
{
|
||||
private readonly ObjectPool<List<Blob>> _blobListPool;
|
||||
private readonly ObjectPool<Queue<(int, int)>> _pixelQueuePool;
|
||||
private readonly ObjectPool<List<(int, int)>> _pixelListPool;
|
||||
private readonly ObjectPool<bool[,]> _visitedArrayPool;
|
||||
|
||||
public ImageProcessingPools()
|
||||
{
|
||||
var provider = new DefaultObjectPoolProvider();
|
||||
_blobListPool = provider.Create<List<Blob>>();
|
||||
_pixelQueuePool = provider.Create<Queue<(int, int)>>();
|
||||
_pixelListPool = provider.Create<List<(int, int)>>();
|
||||
_visitedArrayPool = provider.Create(new VisitedArrayPolicy());
|
||||
}
|
||||
|
||||
// ✅ 池化的图像分割方法
|
||||
public List<Blob> PerformConnectedComponentAnalysis(InfraredImage image)
|
||||
{
|
||||
int width = image.Width;
|
||||
int height = image.Height;
|
||||
|
||||
var blobs = _blobListPool.Get();
|
||||
var queue = _pixelQueuePool.Get();
|
||||
var visited = GetVisitedArray(width, height);
|
||||
|
||||
try
|
||||
{
|
||||
double threshold = CalculateThreshold(image);
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
if (!visited[y, x] && image.GetIntensity(y, x) > threshold)
|
||||
{
|
||||
var pixelList = _pixelListPool.Get();
|
||||
try
|
||||
{
|
||||
var currentBlob = ProcessBlob(x, y, image, visited, queue, pixelList, threshold);
|
||||
blobs.Add(currentBlob);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_pixelListPool.Return(pixelList);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 返回副本,调用者拥有所有权
|
||||
return new List<Blob>(blobs);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_blobListPool.Return(blobs);
|
||||
_pixelQueuePool.Return(queue);
|
||||
ReturnVisitedArray(visited);
|
||||
}
|
||||
}
|
||||
|
||||
private Blob ProcessBlob(int startX, int startY, InfraredImage image, bool[,] visited,
|
||||
Queue<(int, int)> queue, List<(int, int)> pixelList, double threshold)
|
||||
{
|
||||
int width = image.Width;
|
||||
int height = image.Height;
|
||||
|
||||
var blob = new Blob
|
||||
{
|
||||
Label = GetNextBlobLabel(),
|
||||
MinX = startX, MinY = startY, MaxX = startX, MaxY = startY
|
||||
};
|
||||
|
||||
queue.Enqueue((startX, startY));
|
||||
visited[startY, startX] = true;
|
||||
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
var (x, y) = queue.Dequeue();
|
||||
pixelList.Add((x, y));
|
||||
|
||||
// 更新边界框
|
||||
blob.MinX = Math.Min(blob.MinX, x);
|
||||
blob.MinY = Math.Min(blob.MinY, y);
|
||||
blob.MaxX = Math.Max(blob.MaxX, x);
|
||||
blob.MaxY = Math.Max(blob.MaxY, y);
|
||||
|
||||
// 检查4-邻域(使用局部数组避免分配)
|
||||
CheckNeighbor(x, y + 1, width, height, image, visited, queue, threshold);
|
||||
CheckNeighbor(x, y - 1, width, height, image, visited, queue, threshold);
|
||||
CheckNeighbor(x + 1, y, width, height, image, visited, queue, threshold);
|
||||
CheckNeighbor(x - 1, y, width, height, image, visited, queue, threshold);
|
||||
}
|
||||
|
||||
// 复制像素列表到Blob(调用者拥有)
|
||||
blob.Pixels = new List<(int, int)>(pixelList);
|
||||
return blob;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 示例4:制导系统历史数据优化
|
||||
|
||||
### 原始代码问题
|
||||
```csharp
|
||||
// 多个制导系统中的问题
|
||||
private readonly Queue<bool> activeDetectionHistory = new();
|
||||
private readonly Queue<double> lockSnrHistory = new();
|
||||
|
||||
// 每帧操作
|
||||
activeDetectionHistory.Enqueue(currentFrameSuccess);
|
||||
while (activeDetectionHistory.Count > WINDOW_SIZE)
|
||||
activeDetectionHistory.Dequeue(); // 🔴 频繁的内存操作
|
||||
|
||||
double successRate = (double)activeDetectionHistory.Count(s => s) / WINDOW_SIZE; // 🔴 LINQ分配
|
||||
```
|
||||
|
||||
### 优化后的实现
|
||||
```csharp
|
||||
// ✅ 高性能循环缓冲区
|
||||
public struct CircularBuffer<T> where T : struct
|
||||
{
|
||||
private readonly T[] _buffer;
|
||||
private int _head;
|
||||
private int _count;
|
||||
public readonly int Capacity;
|
||||
|
||||
public CircularBuffer(int capacity)
|
||||
{
|
||||
_buffer = new T[capacity];
|
||||
_head = 0;
|
||||
_count = 0;
|
||||
Capacity = capacity;
|
||||
}
|
||||
|
||||
public void Add(T item)
|
||||
{
|
||||
_buffer[(_head + _count) % _buffer.Length] = item;
|
||||
if (_count < _buffer.Length)
|
||||
_count++;
|
||||
else
|
||||
_head = (_head + 1) % _buffer.Length;
|
||||
}
|
||||
|
||||
public int Count => _count;
|
||||
public bool IsFull => _count == _buffer.Length;
|
||||
|
||||
// 零分配的成功率计算
|
||||
public double CalculateBooleanSuccessRate()
|
||||
{
|
||||
if (_count == 0) return 0.0;
|
||||
|
||||
int successCount = 0;
|
||||
for (int i = 0; i < _count; i++)
|
||||
{
|
||||
int index = (_head + i) % _buffer.Length;
|
||||
if (_buffer[index] is bool success && success)
|
||||
successCount++;
|
||||
}
|
||||
return (double)successCount / _count;
|
||||
}
|
||||
|
||||
// 零分配的数值平均值计算
|
||||
public double CalculateAverage()
|
||||
{
|
||||
if (_count == 0) return 0.0;
|
||||
|
||||
double sum = 0.0;
|
||||
for (int i = 0; i < _count; i++)
|
||||
{
|
||||
int index = (_head + i) % _buffer.Length;
|
||||
if (_buffer[index] is double value)
|
||||
sum += value;
|
||||
}
|
||||
return sum / _count;
|
||||
}
|
||||
}
|
||||
|
||||
// 在制导系统中使用
|
||||
public class MillimeterWaveGuidanceSystem : BaseGuidanceSystem
|
||||
{
|
||||
// ✅ 使用循环缓冲区替代Queue
|
||||
private CircularBuffer<bool> _detectionHistory = new(TRACK_DETECTION_WINDOW_SIZE);
|
||||
private CircularBuffer<double> _snrHistory = new(LOCK_SNR_WINDOW_SIZE);
|
||||
|
||||
private bool TryDetectTarget(/* 参数 */)
|
||||
{
|
||||
// ... 检测逻辑
|
||||
|
||||
// ✅ 零分配的历史记录更新
|
||||
_detectionHistory.Add(currentFrameSuccess);
|
||||
|
||||
// ✅ 零分配的成功率计算
|
||||
if (_detectionHistory.IsFull)
|
||||
{
|
||||
double successRate = _detectionHistory.CalculateBooleanSuccessRate();
|
||||
bool overallSuccess = successRate >= SUCCESS_RATE_THRESHOLD;
|
||||
return overallSuccess;
|
||||
}
|
||||
|
||||
return currentFrameSuccess;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 性能对比测试
|
||||
|
||||
### 测试代码示例
|
||||
```csharp
|
||||
[Benchmark]
|
||||
public class SimulationPerformanceTest
|
||||
{
|
||||
private SimulationManager _optimizedManager;
|
||||
private SimulationManager _originalManager;
|
||||
private List<SimulationElement> _testEntities;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
// 创建测试实体
|
||||
_testEntities = CreateTestEntities(1000);
|
||||
|
||||
_optimizedManager = new SimulationManager(); // 使用优化版本
|
||||
_originalManager = new SimulationManager(); // 使用原始版本
|
||||
|
||||
// 注册实体
|
||||
foreach (var entity in _testEntities)
|
||||
{
|
||||
_optimizedManager.RegisterEntity(entity.Id, entity);
|
||||
_originalManager.RegisterEntity(entity.Id, entity);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true)]
|
||||
public void OriginalUpdateSimulation()
|
||||
{
|
||||
_originalManager.Update(0.016); // 60 FPS
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void OptimizedUpdateSimulation()
|
||||
{
|
||||
_optimizedManager.Update(0.016); // 60 FPS
|
||||
}
|
||||
}
|
||||
|
||||
// 预期结果:
|
||||
// | Method | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Allocated |
|
||||
// |-------------------- |----------:|---------:|---------:|------:|--------:|--------:|----------:|
|
||||
// | OriginalSimulation | 1.234 ms | 0.024 ms | 0.021 ms | 1.00 | 150.391 | 75.195 | 2.1 MB |
|
||||
// | OptimizedSimulation | 0.456 ms | 0.009 ms | 0.008 ms | 0.37 | 7.813 | 0.000 | 0.12 MB |
|
||||
```
|
||||
|
||||
## 实施检查清单
|
||||
|
||||
### 阶段1:对象池实施
|
||||
- [ ] 创建SimulationObjectPools类
|
||||
- [ ] 实现ListPooledObjectPolicy<T>
|
||||
- [ ] 修改SimulationManager.UpdateSimulation()
|
||||
- [ ] 修改SimulationManager.CheckHits()
|
||||
- [ ] 添加性能测试
|
||||
- [ ] 验证功能正确性
|
||||
|
||||
### 阶段2:事件系统优化
|
||||
- [ ] 优化PublishEvent方法
|
||||
- [ ] 消除ToList()调用
|
||||
- [ ] 替换foreach为for循环
|
||||
- [ ] 添加类型化的事件处理器缓存
|
||||
- [ ] 测试事件发布性能
|
||||
|
||||
### 阶段3:图像处理优化
|
||||
- [ ] 创建ImageProcessingPools
|
||||
- [ ] 实现池化的连通区域分析
|
||||
- [ ] 优化Blob创建和管理
|
||||
- [ ] 添加循环缓冲区支持
|
||||
- [ ] 性能基准测试
|
||||
|
||||
每个阶段完成后都应该运行完整的测试套件,确保功能正确性不受影响。
|
||||
@ -192,9 +192,9 @@ namespace ThreatSource.Tools.MissileSimulation
|
||||
/// </summary>
|
||||
private void AddWeathers()
|
||||
{
|
||||
// 创建雨天天气并设置为当前天气
|
||||
var rainWeather = _threatSourceFactory.CreateWeather("sunny");
|
||||
simulationManager.SetWeather(rainWeather);
|
||||
// 创建晴天天气并设置为当前天气
|
||||
var sunnyWeather = _threatSourceFactory.CreateWeather("sunny");
|
||||
simulationManager.SetWeather(sunnyWeather);
|
||||
Console.WriteLine("已添加并设置晴天天气环境");
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user