Batch agent path calculation for improved performance

This commit is contained in:
Stone_Red
2026-05-05 18:02:19 +02:00
parent 5730248a3a
commit 8181cde647
@@ -784,7 +784,8 @@ public class LargeGridPathfindingGame : Game
} }
// Efficiently collect agents without paths (avoid LINQ allocation on hot path) // Efficiently collect agents without paths (avoid LINQ allocation on hot path)
List<Agent> agentsRequirePathList = new List<Agent>(Math.Min(agents.Count, 16)); // pre-allocate reasonable capacity List<Agent> agentsRequirePathList = new List<Agent>(agents.Count);
foreach (Agent agent in agents) foreach (Agent agent in agents)
{ {
if (agent.Path is null) if (agent.Path is null)
@@ -796,43 +797,46 @@ public class LargeGridPathfindingGame : Game
if (agentsRequirePathList.Count != 0) if (agentsRequirePathList.Count != 0)
{ {
int agentCount = agentsRequirePathList.Count; int agentCount = agentsRequirePathList.Count;
int batchSize = Math.Max(64, agentCount / (Environment.ProcessorCount * 4));
ProgressTracker.ProgressData progressDataPaths = progressTracker.AddProgress($"Calculating {agentCount} paths", out IProgress<float> progress); ProgressTracker.ProgressData progressDataPaths = progressTracker.AddProgress($"Calculating {agentCount} paths", out IProgress<float> progress);
// Use ParallelOptions to control concurrency
ParallelOptions parallelOptions = new ParallelOptions ParallelOptions parallelOptions = new ParallelOptions
{ {
MaxDegreeOfParallelism = Environment.ProcessorCount MaxDegreeOfParallelism = Environment.ProcessorCount
}; };
// Batch progress reporting every 100ms instead of modulo checks
long lastProgressReport = Environment.TickCount64; long lastProgressReport = Environment.TickCount64;
int pathsCalculated = 0; int pathsCalculated = 0;
_ = Parallel.ForEach(agentsRequirePathList, parallelOptions, agent => _ = Parallel.ForEach(Partitioner.Create(0, agentCount, batchSize), parallelOptions, range =>
{ {
// Calculate path once and reuse for (int i = range.Item1; i < range.Item2; i++)
List<Vector2>? path = CalculatePath(agent.GridPosition, agent.Destination) ?? CalculatePath(); {
Agent agent = agentsRequirePathList[i];
List<Vector2>? path = CalculatePath(agent.GridPosition, agent.Destination)
?? CalculatePath();
agent.Path = path; agent.Path = path;
// Avoid repeated property access and null checks
if (path?.Count > 0) if (path?.Count > 0)
{ {
agent.Position = path[0]; agent.Position = path[0];
agent.NextPosition = path.Count > 1 ? path[1] : path[0]; agent.NextPosition = path.Count > 1 ? path[1] : path[0];
agent.Destination = new Point((int)path[^1].X, (int)path[^1].Y); agent.Destination = new Point((int)path[^1].X, (int)path[^1].Y);
} }
}
// Batch progress updates to reduce lock contention int local = Interlocked.Add(ref pathsCalculated, range.Item2 - range.Item1);
int local = Interlocked.Increment(ref pathsCalculated);
long now = Environment.TickCount64; long now = Environment.TickCount64;
if (now - lastProgressReport > 100) if (now - lastProgressReport > 100)
{ {
progress.Report((float)local / agentCount); progress.Report((float)local / agentCount);
lastProgressReport = now; _ = Interlocked.Exchange(ref lastProgressReport, now);
} }
}); });
// Report final progress
progress.Report(1.0f); progress.Report(1.0f);
progressTracker.RemoveProgress(progressDataPaths); progressTracker.RemoveProgress(progressDataPaths);
} }