perf: SimulationEngine GC优化
CollectSnapshots: 字符串拼接替代 JsonSerializer.Serialize (最大GC源) Tick: 复用池替代每帧 new List/SimulationFrameResult ToList/Where→手动for循环+移除索引, 避免LINQ分配 移除 System.Text.Json 依赖
This commit is contained in:
parent
c379e849f0
commit
cc1a0628d8
@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using CounterDrone.Core.Algorithms;
|
||||
using CounterDrone.Core.Models;
|
||||
using CounterDrone.Core.Services;
|
||||
@ -52,6 +51,13 @@ namespace CounterDrone.Core.Simulation
|
||||
private readonly IDefensePlanner _planner;
|
||||
private int _entityCounter;
|
||||
|
||||
// ── GC 优化:预分配复用池 ──
|
||||
private readonly List<EntitySnapshot> _snapshotPool = new();
|
||||
private readonly List<SimEvent> _frameEventPool = new();
|
||||
private readonly SimulationFrameResult _frameResultPool = new();
|
||||
private readonly List<int> _removalIndices = new();
|
||||
private readonly List<DroneEntity> _flyingDrones = new();
|
||||
|
||||
public SimulationEngine(IScenarioService scenarioService, FrameDataStore frameStore,
|
||||
IDamageModel damageModel, IPathProvider paths, IDefensePlanner planner)
|
||||
{
|
||||
@ -157,12 +163,19 @@ namespace CounterDrone.Core.Simulation
|
||||
public SimulationFrameResult Tick(float deltaTime)
|
||||
{
|
||||
if (State != SimulationState.Running)
|
||||
return new SimulationFrameResult { State = State, FrameIndex = FrameIndex, SimulationTime = SimulationTime };
|
||||
{
|
||||
_frameResultPool.State = State;
|
||||
_frameResultPool.FrameIndex = FrameIndex;
|
||||
_frameResultPool.SimulationTime = SimulationTime;
|
||||
_frameResultPool.EntitySnapshots = null!;
|
||||
_frameResultPool.NewEvents = null!;
|
||||
return _frameResultPool;
|
||||
}
|
||||
|
||||
var scaledDt = deltaTime * TimeScale;
|
||||
SimulationTime += scaledDt;
|
||||
FrameIndex++;
|
||||
var frameEvents = new List<SimEvent>();
|
||||
_frameEventPool.Clear();
|
||||
|
||||
// 1. 按发射计划执行
|
||||
while (_nextFireIndex < _fireSchedule.Count && _fireSchedule[_nextFireIndex].FireTime <= SimulationTime)
|
||||
@ -177,7 +190,6 @@ namespace CounterDrone.Core.Simulation
|
||||
}
|
||||
else if (platform.Ready)
|
||||
{
|
||||
// 地基:直接发射
|
||||
platform.Release();
|
||||
var m = new MunitionEntity($"mun_{++_entityCounter}",
|
||||
platform.PlatformType ?? Models.PlatformType.GroundBased,
|
||||
@ -187,13 +199,15 @@ namespace CounterDrone.Core.Simulation
|
||||
fe.FireTime);
|
||||
_munitions.Add(m);
|
||||
OnMunitionLaunched?.Invoke(m);
|
||||
frameEvents.Add(new SimEvent { Type = SimEventType.MunitionLaunched, OccurredAt = fe.FireTime, SourceId = platform.Id, Description = $"计划发射 {m.Id} @({platform.PosX:F0},{platform.PosY:F0},{platform.PosZ:F0})→({fe.TargetX:F0},{fe.TargetY:F0},{fe.TargetZ:F0})" });
|
||||
_frameEventPool.Add(new SimEvent { Type = SimEventType.MunitionLaunched, OccurredAt = fe.FireTime, SourceId = platform.Id, Description = $"计划发射 {m.Id} @({platform.PosX:F0},{platform.PosY:F0},{platform.PosZ:F0})→({fe.TargetX:F0},{fe.TargetY:F0},{fe.TargetZ:F0})" });
|
||||
}
|
||||
}
|
||||
|
||||
// 1b. 空基平台到达投放点 → 投弹
|
||||
foreach (var platform in _platforms.Where(p => p.CanRelease))
|
||||
// 1b. 空基平台到达投放点 → 投弹(手动遍历避免 LINQ)
|
||||
for (int i = 0; i < _platforms.Count; i++)
|
||||
{
|
||||
var platform = _platforms[i];
|
||||
if (!platform.CanRelease) continue;
|
||||
var (cvx, cvy, cvz) = platform.CurrentVelocity;
|
||||
platform.Release();
|
||||
var m = new MunitionEntity($"mun_{++_entityCounter}",
|
||||
@ -206,12 +220,14 @@ namespace CounterDrone.Core.Simulation
|
||||
cvx, cvy, cvz);
|
||||
_munitions.Add(m);
|
||||
OnMunitionLaunched?.Invoke(m);
|
||||
frameEvents.Add(new SimEvent { Type = SimEventType.MunitionLaunched, OccurredAt = platform.ExactReleaseTime, SourceId = platform.Id, Description = $"空基投放 {m.Id}" });
|
||||
_frameEventPool.Add(new SimEvent { Type = SimEventType.MunitionLaunched, OccurredAt = platform.ExactReleaseTime, SourceId = platform.Id, Description = $"空基投放 {m.Id}" });
|
||||
}
|
||||
|
||||
// 2. 弹药飞行 → 云团生成
|
||||
foreach (var m in _munitions.ToList())
|
||||
// 2. 弹药飞行 → 云团生成(手动遍历+移除索引,避免 ToList())
|
||||
_removalIndices.Clear();
|
||||
for (int i = 0; i < _munitions.Count; i++)
|
||||
{
|
||||
var m = _munitions[i];
|
||||
m.Update(scaledDt);
|
||||
if (m.HasArrived)
|
||||
{
|
||||
@ -219,49 +235,54 @@ namespace CounterDrone.Core.Simulation
|
||||
disp.Initialize(_ammoSpec, _scene, new Algorithms.Vector3(m.PosX, m.PosY, m.PosZ), m.ArrivalTime);
|
||||
var cloud = new CloudEntity($"cloud_{++_entityCounter}", m.AerosolType, disp, m.ArrivalTime);
|
||||
_clouds.Add(cloud);
|
||||
_munitions.Remove(m);
|
||||
OnCloudGenerated?.Invoke(cloud);
|
||||
frameEvents.Add(new SimEvent { Type = SimEventType.CloudGenerated, OccurredAt = m.ArrivalTime, Description = $"云团生成 @({m.PosX:F0},{m.PosY:F0},{m.PosZ:F0})" });
|
||||
_frameEventPool.Add(new SimEvent { Type = SimEventType.CloudGenerated, OccurredAt = m.ArrivalTime, Description = $"云团生成 @({m.PosX:F0},{m.PosY:F0},{m.PosZ:F0})" });
|
||||
_removalIndices.Add(i);
|
||||
}
|
||||
}
|
||||
for (int i = _removalIndices.Count - 1; i >= 0; i--)
|
||||
_munitions.RemoveAt(_removalIndices[i]);
|
||||
|
||||
// 3. 云团演化
|
||||
foreach (var c in _clouds.ToList())
|
||||
// 3. 云团演化(手动遍历+移除索引)
|
||||
_removalIndices.Clear();
|
||||
for (int i = 0; i < _clouds.Count; i++)
|
||||
{
|
||||
var c = _clouds[i];
|
||||
c.Tick(scaledDt, (float)_scene.WindSpeed, (WindDirection)_scene.WindDirection);
|
||||
if (c.IsDissipated) _clouds.Remove(c);
|
||||
if (c.IsDissipated) _removalIndices.Add(i);
|
||||
}
|
||||
for (int i = _removalIndices.Count - 1; i >= 0; i--)
|
||||
_clouds.RemoveAt(_removalIndices[i]);
|
||||
|
||||
// 4. 毁伤判定(路径积分替代点采样)
|
||||
foreach (var drone in _drones.Where(d => d.Status == DroneStatus.Flying))
|
||||
{
|
||||
// 保存上一帧位置用于积分
|
||||
drone.SavePreviousPosition();
|
||||
}
|
||||
// 4-5. 无人机:缓存飞行状态列表,避免多次 Where
|
||||
_flyingDrones.Clear();
|
||||
for (int i = 0; i < _drones.Count; i++)
|
||||
if (_drones[i].Status == DroneStatus.Flying)
|
||||
_flyingDrones.Add(_drones[i]);
|
||||
|
||||
// 5. 无人机移动
|
||||
foreach (var drone in _drones.Where(d => d.Status == DroneStatus.Flying))
|
||||
for (int i = 0; i < _flyingDrones.Count; i++)
|
||||
_flyingDrones[i].SavePreviousPosition();
|
||||
|
||||
for (int i = 0; i < _flyingDrones.Count; i++)
|
||||
{
|
||||
drone.Update(scaledDt);
|
||||
if (drone.Status == DroneStatus.ReachedTarget)
|
||||
_flyingDrones[i].Update(scaledDt);
|
||||
if (_flyingDrones[i].Status == DroneStatus.ReachedTarget)
|
||||
{
|
||||
OnDroneReachedTarget?.Invoke(drone);
|
||||
frameEvents.Add(new SimEvent { Type = SimEventType.DroneReachedTarget, OccurredAt = SimulationTime, SourceId = drone.Id, Description = "到达攻击目标" });
|
||||
OnDroneReachedTarget?.Invoke(_flyingDrones[i]);
|
||||
_frameEventPool.Add(new SimEvent { Type = SimEventType.DroneReachedTarget, OccurredAt = SimulationTime, SourceId = _flyingDrones[i].Id, Description = "到达攻击目标" });
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 毁伤判定:积分路径段在云内的时间
|
||||
// 在云团参考系计算:云团中心在该参考系为原点。
|
||||
// 云团本 tick 内也在移动(风偏),故 Prev 帧的云团中心 = 本帧中心 − 风位移。
|
||||
// 不做此修正的话 PathInSphere 会把云团当静止球,每 tick 引入 ~2m 误差。
|
||||
var (wvx, wvy, wvz) = Kinematics.WindToVector((WindDirection)_scene.WindDirection, (float)_scene.WindSpeed);
|
||||
float cloudDx = wvx * scaledDt, cloudDy = wvy * scaledDt, cloudDz = wvz * scaledDt;
|
||||
foreach (var drone in _drones.Where(d => d.Status == DroneStatus.Flying))
|
||||
for (int di = 0; di < _flyingDrones.Count; di++)
|
||||
{
|
||||
foreach (var cloud in _clouds)
|
||||
var drone = _flyingDrones[di];
|
||||
for (int ci = 0; ci < _clouds.Count; ci++)
|
||||
{
|
||||
var cloud = _clouds[ci];
|
||||
float ccx = cloud.Dispersion.Center.X, ccy = cloud.Dispersion.Center.Y, ccz = cloud.Dispersion.Center.Z;
|
||||
// 无人机线段换算到云团参考系:相对位置 = drone - cloud
|
||||
float inCloudDistance = DamageAssessment.PathInSphere(
|
||||
drone.PrevX - (ccx - cloudDx), drone.PrevY - (ccy - cloudDy), drone.PrevZ - (ccz - cloudDz),
|
||||
drone.PosX - ccx, drone.PosY - ccy, drone.PosZ - ccz,
|
||||
@ -276,45 +297,54 @@ namespace CounterDrone.Core.Simulation
|
||||
if (drone.Status == DroneStatus.Destroyed)
|
||||
{
|
||||
OnDroneDestroyed?.Invoke(drone);
|
||||
frameEvents.Add(new SimEvent { Type = SimEventType.DroneDestroyed, OccurredAt = SimulationTime, TargetId = drone.Id, Description = $"无人机 {drone.Id} 被摧毁 @({drone.PosX:F0},{drone.PosY:F0},{drone.PosZ:F0})" });
|
||||
break; // 已击毁,不再判定本 tick 其他云团,避免重复触发
|
||||
_frameEventPool.Add(new SimEvent { Type = SimEventType.DroneDestroyed, OccurredAt = SimulationTime, TargetId = drone.Id, Description = $"无人机 {drone.Id} 被摧毁 @({drone.PosX:F0},{drone.PosY:F0},{drone.PosZ:F0})" });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 平台冷却
|
||||
foreach (var p in _platforms) p.Update(scaledDt);
|
||||
// 7. 平台冷却
|
||||
for (int i = 0; i < _platforms.Count; i++) _platforms[i].Update(scaledDt);
|
||||
|
||||
// 7. 管控区域
|
||||
foreach (var drone in _drones.Where(d => d.Status == DroneStatus.Flying))
|
||||
// 8. 管控区域
|
||||
for (int di = 0; di < _flyingDrones.Count; di++)
|
||||
{
|
||||
foreach (var zone in _zones)
|
||||
var drone = _flyingDrones[di];
|
||||
for (int zi = 0; zi < _zones.Count; zi++)
|
||||
{
|
||||
if (zone.ContainsPoint(drone.PosX, drone.PosY, drone.PosZ))
|
||||
if (_zones[zi].ContainsPoint(drone.PosX, drone.PosY, drone.PosZ))
|
||||
{
|
||||
drone.MarkZoneIntruded();
|
||||
OnZoneIntruded?.Invoke(drone, zone);
|
||||
frameEvents.Add(new SimEvent { Type = SimEventType.ZoneIntruded, OccurredAt = SimulationTime, SourceId = drone.Id, TargetId = zone.Id, Description = $"侵入 {zone.Name}" });
|
||||
OnZoneIntruded?.Invoke(drone, _zones[zi]);
|
||||
_frameEventPool.Add(new SimEvent { Type = SimEventType.ZoneIntruded, OccurredAt = SimulationTime, SourceId = drone.Id, TargetId = _zones[zi].Id, Description = $"侵入 {_zones[zi].Name}" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 8. 结束判定
|
||||
if (_drones.All(d => d.Status != DroneStatus.Flying))
|
||||
// 9. 结束判定
|
||||
bool allDone = true;
|
||||
for (int i = 0; i < _drones.Count; i++)
|
||||
if (_drones[i].Status == DroneStatus.Flying) { allDone = false; break; }
|
||||
if (allDone)
|
||||
{
|
||||
State = SimulationState.Completed;
|
||||
OnSimulationEnded?.Invoke();
|
||||
frameEvents.Add(new SimEvent { Type = SimEventType.SimulationEnd, OccurredAt = SimulationTime });
|
||||
_frameEventPool.Add(new SimEvent { Type = SimEventType.SimulationEnd, OccurredAt = SimulationTime });
|
||||
}
|
||||
|
||||
_allEvents.AddRange(frameEvents);
|
||||
_allEvents.AddRange(_frameEventPool);
|
||||
|
||||
var snapshots = CollectSnapshots();
|
||||
_frameStore.WriteFrame(_frameDb, _taskId, FrameIndex, SimulationTime, snapshots);
|
||||
if (FrameIndex % 50 == 0) _frameDb.Commit();
|
||||
|
||||
return new SimulationFrameResult { EntitySnapshots = snapshots, NewEvents = frameEvents, State = State, FrameIndex = FrameIndex, SimulationTime = SimulationTime };
|
||||
_frameResultPool.EntitySnapshots = snapshots;
|
||||
_frameResultPool.NewEvents = _frameEventPool;
|
||||
_frameResultPool.State = State;
|
||||
_frameResultPool.FrameIndex = FrameIndex;
|
||||
_frameResultPool.SimulationTime = SimulationTime;
|
||||
return _frameResultPool;
|
||||
}
|
||||
|
||||
public void Pause() { if (State == SimulationState.Running) State = SimulationState.Paused; }
|
||||
@ -402,14 +432,24 @@ namespace CounterDrone.Core.Simulation
|
||||
|
||||
private List<EntitySnapshot> CollectSnapshots()
|
||||
{
|
||||
var list = new List<EntitySnapshot>();
|
||||
foreach (var d in _drones)
|
||||
list.Add(new EntitySnapshot { EntityId = d.Id, EntityType = Models.EntityType.Drone, PosX = d.PosX, PosY = d.PosY, PosZ = d.PosZ, StateData = JsonSerializer.Serialize(new { damageStage = (int)_damageModel.GetDamageStage(1 - d.Hp), hp = d.Hp }) });
|
||||
foreach (var p in _platforms.Where(p => p.PlatformType == Models.PlatformType.AirBased))
|
||||
list.Add(new EntitySnapshot { EntityId = p.Id, EntityType = Models.EntityType.Platform, PosX = p.PosX, PosY = p.PosY, PosZ = p.PosZ, StateData = JsonSerializer.Serialize(new { state = p.State.ToString() }) });
|
||||
foreach (var c in _clouds)
|
||||
list.Add(new EntitySnapshot { EntityId = c.Id, EntityType = Models.EntityType.Cloud, PosX = c.Dispersion.Center.X, PosY = c.Dispersion.Center.Y, PosZ = c.Dispersion.Center.Z, StateData = JsonSerializer.Serialize(new { radius = c.Dispersion.Radius, opacity = c.Dispersion.Particles.Opacity, phase = c.Dispersion.Phase, elapsed = c.Dispersion.Elapsed }) });
|
||||
return list;
|
||||
_snapshotPool.Clear();
|
||||
for (int i = 0; i < _drones.Count; i++)
|
||||
{
|
||||
var d = _drones[i];
|
||||
_snapshotPool.Add(new EntitySnapshot { EntityId = d.Id, EntityType = Models.EntityType.Drone, PosX = d.PosX, PosY = d.PosY, PosZ = d.PosZ, StateData = "{\"damageStage\":" + (int)_damageModel.GetDamageStage(1 - d.Hp) + ",\"hp\":" + d.Hp.ToString("F2") + "}" });
|
||||
}
|
||||
for (int i = 0; i < _platforms.Count; i++)
|
||||
{
|
||||
var p = _platforms[i];
|
||||
if (p.PlatformType != Models.PlatformType.AirBased) continue;
|
||||
_snapshotPool.Add(new EntitySnapshot { EntityId = p.Id, EntityType = Models.EntityType.Platform, PosX = p.PosX, PosY = p.PosY, PosZ = p.PosZ, StateData = "{\"state\":\"" + p.State + "\"}" });
|
||||
}
|
||||
for (int i = 0; i < _clouds.Count; i++)
|
||||
{
|
||||
var c = _clouds[i];
|
||||
_snapshotPool.Add(new EntitySnapshot { EntityId = c.Id, EntityType = Models.EntityType.Cloud, PosX = c.Dispersion.Center.X, PosY = c.Dispersion.Center.Y, PosZ = c.Dispersion.Center.Z, StateData = "{\"radius\":" + c.Dispersion.Radius.ToString("F1") + ",\"opacity\":" + c.Dispersion.Particles.Opacity.ToString("F2") + ",\"phase\":" + c.Dispersion.Phase + ",\"elapsed\":" + c.Dispersion.Elapsed.ToString("F1") + "}" });
|
||||
}
|
||||
return _snapshotPool;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user