From 6b257a9661ca3e23aea53ac79bb0291fd75636a7 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Wed, 23 Oct 2019 17:31:22 -0700 Subject: [PATCH] more GC optimizations --- Graph/DirectedGraph.cs | 32 +++++++++++----------- Graph/DirectedWeightedGraph.cs | 42 ++++++++++++++++++----------- Graph/DirectedWeightedMultiGraph.cs | 35 ++++++++++++++++++------ test/DirectedWeightedGraph.cs | 3 ++- test/DirectedWeightedMultiGraph.cs | 3 ++- 5 files changed, 74 insertions(+), 41 deletions(-) diff --git a/Graph/DirectedGraph.cs b/Graph/DirectedGraph.cs index fa267ff..9de68c9 100644 --- a/Graph/DirectedGraph.cs +++ b/Graph/DirectedGraph.cs @@ -280,7 +280,7 @@ namespace MoonTools.Core.Graph s.Remove(neighbor); t.Add(neighbor); - if (s.Count == 0) { lexicographicSets.Remove(s); replacedSets.Remove(s); } + if (s.Count == 0) { lexicographicSets.Remove(s); replacedSets.Remove(s); s.Dispose(); } } } } @@ -298,20 +298,20 @@ namespace MoonTools.Core.Graph { return PostorderNodeDFS().Reverse(); } - - readonly Dictionary preorder = new Dictionary(); - readonly Dictionary lowlink = new Dictionary(); - readonly Dictionary sccFound = new Dictionary(); - readonly Stack sccQueue = new Stack(); - readonly List> sccResult = new List>(); + List> sccs = new List>(); public IEnumerable> StronglyConnectedComponents() { - preorder.Clear(); - lowlink.Clear(); - sccFound.Clear(); - sccQueue.Clear(); - sccResult.Clear(); + foreach (var scc in sccs) + { + scc.Dispose(); + } + sccs.Clear(); + + var preorder = new PooledDictionary(); + var lowlink = new PooledDictionary(); + var sccFound = new PooledDictionary(); + var sccQueue = new PooledStack(); uint preorderCounter = 0; @@ -364,7 +364,7 @@ namespace MoonTools.Core.Graph if (lowlink[v] == preorder[v]) { sccFound[v] = true; - var scc = new List + var scc = new PooledList { v }; @@ -374,7 +374,9 @@ namespace MoonTools.Core.Graph sccFound[k] = true; scc.Add(k); } - sccResult.Add(scc); + + sccs.Add(scc); + yield return scc; } else { @@ -384,8 +386,6 @@ namespace MoonTools.Core.Graph } } } - - return sccResult; } public IEnumerable> SimpleCycles() diff --git a/Graph/DirectedWeightedGraph.cs b/Graph/DirectedWeightedGraph.cs index c7b8671..ea743e2 100644 --- a/Graph/DirectedWeightedGraph.cs +++ b/Graph/DirectedWeightedGraph.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Collections.Pooled; using MoreLinq; namespace MoonTools.Core.Graph @@ -12,13 +13,6 @@ namespace MoonTools.Core.Graph protected Dictionary<(TNode, TNode), TEdgeData> edgeToEdgeData = new Dictionary<(TNode, TNode), TEdgeData>(); protected Dictionary<(TNode, TNode), int> weights = new Dictionary<(TNode, TNode), int>(); - // store search sets to prevent GC - protected HashSet openSet = new HashSet(); - protected HashSet closedSet = new HashSet(); - protected Dictionary gScore = new Dictionary(); - protected Dictionary fScore = new Dictionary(); - protected Dictionary cameFrom = new Dictionary(); - public IEnumerable Nodes => nodes; public void AddNode(TNode node) @@ -107,7 +101,7 @@ namespace MoonTools.Core.Graph return edgeToEdgeData[(v, u)]; } - private IEnumerable<(TNode, TNode)> ReconstructPath(Dictionary cameFrom, TNode currentNode) + private IEnumerable<(TNode, TNode)> ReconstructPath(PooledDictionary cameFrom, TNode currentNode) { while (cameFrom.ContainsKey(currentNode)) { @@ -121,11 +115,11 @@ namespace MoonTools.Core.Graph { CheckNodes(start, end); - openSet.Clear(); - closedSet.Clear(); - gScore.Clear(); - fScore.Clear(); - cameFrom.Clear(); + var openSet = new PooledSet(ClearMode.Always); + var closedSet = new PooledSet(ClearMode.Always); + var gScore = new PooledDictionary(ClearMode.Always); + var fScore = new PooledDictionary(ClearMode.Always); + var cameFrom = new PooledDictionary(ClearMode.Always); openSet.Add(start); @@ -138,7 +132,19 @@ namespace MoonTools.Core.Graph if (currentNode.Equals(end)) { - return ReconstructPath(cameFrom, currentNode).Reverse(); + openSet.Dispose(); + closedSet.Dispose(); + gScore.Dispose(); + fScore.Dispose(); + + foreach (var edge in ReconstructPath(cameFrom, currentNode).Reverse()) + { + yield return edge; + } + + cameFrom.Dispose(); + + yield break; } openSet.Remove(currentNode); @@ -163,7 +169,13 @@ namespace MoonTools.Core.Graph } } - return Enumerable.Empty<(TNode, TNode)>(); + openSet.Dispose(); + closedSet.Dispose(); + gScore.Dispose(); + fScore.Dispose(); + cameFrom.Dispose(); + + yield break; } } } \ No newline at end of file diff --git a/Graph/DirectedWeightedMultiGraph.cs b/Graph/DirectedWeightedMultiGraph.cs index 9cb4a11..a27f90a 100644 --- a/Graph/DirectedWeightedMultiGraph.cs +++ b/Graph/DirectedWeightedMultiGraph.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Collections.Pooled; using MoreLinq; namespace MoonTools.Core.Graph @@ -120,7 +121,7 @@ namespace MoonTools.Core.Graph return edgeToEdgeData[id]; } - private IEnumerable ReconstructPath(Dictionary cameFrom, TNode currentNode) + private IEnumerable ReconstructPath(PooledDictionary cameFrom, TNode currentNode) { while (cameFrom.ContainsKey(currentNode)) { @@ -135,11 +136,11 @@ namespace MoonTools.Core.Graph { CheckNodes(start, end); - openSet.Clear(); - closedSet.Clear(); - gScore.Clear(); - fScore.Clear(); - cameFrom.Clear(); + var openSet = new PooledSet(ClearMode.Always); + var closedSet = new PooledSet(ClearMode.Always); + var gScore = new PooledDictionary(ClearMode.Always); + var fScore = new PooledDictionary(ClearMode.Always); + var cameFrom = new PooledDictionary(ClearMode.Always); openSet.Add(start); @@ -152,7 +153,19 @@ namespace MoonTools.Core.Graph if (currentNode.Equals(end)) { - return ReconstructPath(cameFrom, currentNode).Reverse(); + openSet.Dispose(); + closedSet.Dispose(); + gScore.Dispose(); + fScore.Dispose(); + + foreach (var edgeID in ReconstructPath(cameFrom, currentNode).Reverse()) + { + yield return edgeID; + } + + cameFrom.Dispose(); + + yield break; } openSet.Remove(currentNode); @@ -178,7 +191,13 @@ namespace MoonTools.Core.Graph } } - return Enumerable.Empty(); + openSet.Dispose(); + closedSet.Dispose(); + gScore.Dispose(); + fScore.Dispose(); + cameFrom.Dispose(); + + yield break; } } } \ No newline at end of file diff --git a/test/DirectedWeightedGraph.cs b/test/DirectedWeightedGraph.cs index 887e582..34d369a 100644 --- a/test/DirectedWeightedGraph.cs +++ b/test/DirectedWeightedGraph.cs @@ -218,7 +218,8 @@ namespace Tests .And .HaveCount(3); - myGraph.Invoking(x => x.AStarShortestPath('a', 'z', (x, y) => 15)).Should().Throw(); + // have to call Count() because otherwise the lazy evaluation wont trigger + myGraph.Invoking(x => x.AStarShortestPath('a', 'z', (x, y) => 1).Count()).Should().Throw(); } } } \ No newline at end of file diff --git a/test/DirectedWeightedMultiGraph.cs b/test/DirectedWeightedMultiGraph.cs index 06430da..18b67a4 100644 --- a/test/DirectedWeightedMultiGraph.cs +++ b/test/DirectedWeightedMultiGraph.cs @@ -248,7 +248,8 @@ namespace Tests .And .HaveCount(3); - myGraph.Invoking(x => x.AStarShortestPath('a', 'z', (x, y) => 15)).Should().Throw(); + // have to call Count() because otherwise the lazy evaluation wont trigger + myGraph.Invoking(x => x.AStarShortestPath('a', 'z', (x, y) => 1).Count()).Should().Throw(); } } } \ No newline at end of file