518 lines
21 KiB
C#
518 lines
21 KiB
C#
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; }
|
||
}
|
||
} |