Remove path caching.
The path cache was originally a moderate benefit, but over time a couple of things have conspired against it: - Only paths with BlockedByActor.None are cached. Originally all paths regardless of blocking were cached but this was deemed unacceptable due to potentially returning outdated paths as actors move about. Paths with BlockedByActor.None are only invalidated if terrain conditions change, which are rarer. - Move will try and find a path 4 times, trying with a different BlockedByActor check each time. BlockedByActor.None is the last check and only reached if the other searches fail. This is a rare scenario. Overall, this means the hit rate for the cache is almost non-existent. Given the constraints on path validity it seems unlikely that the hit rate could be improved significantly, therefore it seems reasonable to remove the cache entirely to remove the overhead of cache management.
This commit is contained in:
@@ -1,20 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2021 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public interface ICacheStorage<T>
|
||||
{
|
||||
void Remove(string key);
|
||||
void Store(string key, T data);
|
||||
T Retrieve(string key);
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2021 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRA.Mods.Common.Pathfinder
|
||||
{
|
||||
public class PathCacheStorage : ICacheStorage<List<CPos>>
|
||||
{
|
||||
class CachedPath
|
||||
{
|
||||
public List<CPos> Result;
|
||||
public int Tick;
|
||||
}
|
||||
|
||||
const int MaxPathAge = 50;
|
||||
readonly World world;
|
||||
Dictionary<string, CachedPath> cachedPaths = new Dictionary<string, CachedPath>(100);
|
||||
|
||||
public PathCacheStorage(World world)
|
||||
{
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
public void Remove(string key)
|
||||
{
|
||||
cachedPaths.Remove(key);
|
||||
}
|
||||
|
||||
public void Store(string key, List<CPos> data)
|
||||
{
|
||||
// Eventually clean up the cachedPaths dictionary
|
||||
if (cachedPaths.Count >= 100)
|
||||
foreach (var cachedPath in cachedPaths.Where(p => IsExpired(p.Value)).ToList())
|
||||
cachedPaths.Remove(cachedPath.Key);
|
||||
|
||||
cachedPaths.Add(key, new CachedPath
|
||||
{
|
||||
Tick = world.WorldTick,
|
||||
Result = data
|
||||
});
|
||||
}
|
||||
|
||||
public List<CPos> Retrieve(string key)
|
||||
{
|
||||
if (cachedPaths.TryGetValue(key, out var cached))
|
||||
{
|
||||
if (IsExpired(cached))
|
||||
{
|
||||
cachedPaths.Remove(key);
|
||||
return null;
|
||||
}
|
||||
|
||||
return cached.Result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
bool IsExpired(CachedPath path)
|
||||
{
|
||||
return world.WorldTick - path.Tick > MaxPathAge;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2021 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Support;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Pathfinder
|
||||
{
|
||||
/// <summary>
|
||||
/// A decorator used to cache FindUnitPath and FindUnitPathToRange (Decorator design pattern)
|
||||
/// </summary>
|
||||
public class PathFinderUnitPathCacheDecorator : IPathFinder
|
||||
{
|
||||
readonly IPathFinder pathFinder;
|
||||
readonly ICacheStorage<List<CPos>> cacheStorage;
|
||||
|
||||
public PathFinderUnitPathCacheDecorator(IPathFinder pathFinder, ICacheStorage<List<CPos>> cacheStorage)
|
||||
{
|
||||
this.pathFinder = pathFinder;
|
||||
this.cacheStorage = cacheStorage;
|
||||
}
|
||||
|
||||
public List<CPos> FindUnitPath(CPos source, CPos target, Actor self, Actor ignoreActor, BlockedByActor check)
|
||||
{
|
||||
using (new PerfSample("Pathfinder"))
|
||||
{
|
||||
var key = "FindUnitPath" + self.ActorID + source.X + source.Y + target.X + target.Y;
|
||||
|
||||
// Only cache path when transient actors are ignored, otherwise there is no guarantee that the path
|
||||
// is still valid at the next check.
|
||||
if (check == BlockedByActor.None)
|
||||
{
|
||||
var cachedPath = cacheStorage.Retrieve(key);
|
||||
if (cachedPath != null)
|
||||
return cachedPath;
|
||||
}
|
||||
|
||||
var pb = pathFinder.FindUnitPath(source, target, self, ignoreActor, check);
|
||||
|
||||
if (check == BlockedByActor.None)
|
||||
cacheStorage.Store(key, pb);
|
||||
|
||||
return pb;
|
||||
}
|
||||
}
|
||||
|
||||
public List<CPos> FindUnitPathToRange(CPos source, SubCell srcSub, WPos target, WDist range, Actor self, BlockedByActor check)
|
||||
{
|
||||
using (new PerfSample("Pathfinder"))
|
||||
{
|
||||
var key = "FindUnitPathToRange" + self.ActorID + source.X + source.Y + target.X + target.Y;
|
||||
|
||||
if (check == BlockedByActor.None)
|
||||
{
|
||||
var cachedPath = cacheStorage.Retrieve(key);
|
||||
if (cachedPath != null)
|
||||
return cachedPath;
|
||||
}
|
||||
|
||||
var pb = pathFinder.FindUnitPathToRange(source, srcSub, target, range, self, check);
|
||||
|
||||
if (check == BlockedByActor.None)
|
||||
cacheStorage.Store(key, pb);
|
||||
|
||||
return pb;
|
||||
}
|
||||
}
|
||||
|
||||
public List<CPos> FindPath(IPathSearch search)
|
||||
{
|
||||
using (new PerfSample("Pathfinder"))
|
||||
return pathFinder.FindPath(search);
|
||||
}
|
||||
|
||||
public List<CPos> FindBidiPath(IPathSearch fromSrc, IPathSearch fromDest)
|
||||
{
|
||||
using (new PerfSample("Pathfinder"))
|
||||
return pathFinder.FindBidiPath(fromSrc, fromDest);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
public override object Create(ActorInitializer init)
|
||||
{
|
||||
return new PathFinderUnitPathCacheDecorator(new PathFinder(init.World), new PathCacheStorage(init.World));
|
||||
return new PathFinder(init.World);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user