ThreatSourceLibaray/ThreatSource.Tests/src/Simulation/SimulationManagerPerformanceTest.cs

518 lines
21 KiB
C#
Raw 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.

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; }
}
}