Skip to content

Commit 8a10579

Browse files
Akkermanhazzik
Akkerman
authored andcommitted
NH-3777: Replaced NHibernate.Loader.TopologicalSorter by a more performant implementation.
- Because of a slight change in the (internal) Interface of TopologicalSorter, JoinWalker had to be adjusted (note that the removed parameter i was identical to the return value for the current use of the sorter)
1 parent 1920d17 commit 8a10579

File tree

2 files changed

+83
-86
lines changed

2 files changed

+83
-86
lines changed

src/NHibernate/Loader/JoinWalker.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ private static int[] GetTopologicalSortOrder(List<DependentAlias> fields)
184184
// add vertices
185185
for (int i = 0; i < fields.Count; i++)
186186
{
187-
_indexes[fields[i].Alias.ToLower()] = g.AddVertex(i);
187+
_indexes[fields[i].Alias.ToLower()] = g.AddVertex();
188188
}
189189

190190
// add edges
Lines changed: 82 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,122 +1,119 @@
11
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Text;
52

6-
// Algorithm from: http://tawani.blogspot.com/2009/02/topological-sorting-and-cyclic.html
73
namespace NHibernate.Loader
84
{
95
class TopologicalSorter
106
{
11-
#region - Private Members -
7+
sealed class Node
8+
{
9+
public int Index { get; private set; }
10+
public int SuccessorCount { get; private set; }
11+
public bool Eliminated { get; private set; }
12+
System.Action _onEliminate;
13+
14+
public Node(int index)
15+
{
16+
Index = index;
17+
SuccessorCount = 0;
18+
Eliminated = false;
19+
_onEliminate = null;
20+
}
1221

13-
private readonly int[] _vertices; // list of vertices
14-
private readonly int[,] _matrix; // adjacency matrix
15-
private int _numVerts; // current number of vertices
16-
private readonly int[] _sortedArray;
22+
public void RegisterSuccessor(Node successor)
23+
{
24+
SuccessorCount++;
25+
successor._onEliminate += () => SuccessorCount--;
26+
}
1727

18-
#endregion
28+
public void Eliminate()
29+
{
30+
if (_onEliminate != null)
31+
{
32+
_onEliminate();
33+
_onEliminate = null;
34+
}
35+
Eliminated = true;
36+
}
37+
}
1938

20-
#region - CTors -
39+
readonly Node[] _nodes;
40+
int _nodeCount;
2141

2242
public TopologicalSorter(int size)
2343
{
24-
_vertices = new int[size];
25-
_matrix = new int[size, size];
26-
_numVerts = 0;
27-
for (int i = 0; i < size; i++)
28-
for (int j = 0; j < size; j++)
29-
_matrix[i, j] = 0;
30-
_sortedArray = new int[size]; // sorted vert labels
44+
_nodes = new Node[size];
45+
_nodeCount = 0;
3146
}
3247

33-
#endregion
34-
35-
#region - Public Methods -
48+
/// <summary>
49+
/// Adds a new node
50+
/// </summary>
51+
/// <returns>index of the new node</returns>
52+
public int AddVertex()
53+
{
54+
// note: this method cannot add nodes beyond the initial size defined in the constructor.
55+
var node = new Node(_nodeCount++);
56+
_nodes[node.Index] = node;
57+
return node.Index;
58+
}
3659

37-
public int AddVertex(int vertex)
60+
/// <summary>
61+
/// Adds an edge from the node with the given sourceNodeIndex to the node with the given destinationNodeIndex
62+
/// </summary>
63+
/// <param name="sourceNodeIndex">index of a previously added node that is the source of the edge</param>
64+
/// <param name="destinationNodeIndex">index of a previously added node the is the destination of the edge</param>
65+
public void AddEdge(int sourceNodeIndex, int destinationNodeIndex)
3866
{
39-
_vertices[_numVerts++] = vertex;
40-
return _numVerts - 1;
67+
// note: invalid values for "sourceNodeIndex" and "destinationNodeIndex" will either lead to an "IndexOutOfRangeException" or a "NullReferenceException"
68+
_nodes[sourceNodeIndex].RegisterSuccessor(_nodes[destinationNodeIndex]);
4169
}
4270

43-
public void AddEdge(int start, int end)
71+
void EliminateNode(Node node)
4472
{
45-
_matrix[start, end] = 1;
73+
node.Eliminate();
74+
75+
// decrease _nodeCount whenever the last nodes are already eliminated.
76+
// This should save time for the following checks in "getNodeWithoutSuccessors".
77+
while (_nodeCount > 0 && _nodes[_nodeCount - 1].Eliminated)
78+
_nodeCount--;
4679
}
4780

48-
public int[] Sort() // toplogical sort
81+
Node GetNodeWithoutSuccessors()
4982
{
50-
while (_numVerts > 0) // while vertices remain,
83+
// note: Check the nodes in reverse order, since that allows decreases of "_nodeCount" in "eliminateNode"
84+
// as often as possible because high indices are preferred over low indices whenever there is a choice.
85+
for (int i = _nodeCount - 1; i >= 0; i--)
5186
{
52-
// get a vertex with no successors, or -1
53-
int currentVertex = noSuccessors();
54-
if (currentVertex == -1) // must be a cycle
55-
throw new Exception("Graph has cycles");
87+
var node = _nodes[i];
88+
if (node.Eliminated)
89+
continue;
5690

57-
// insert vertex label in sorted array (start at end)
58-
_sortedArray[_numVerts - 1] = _vertices[currentVertex];
91+
if (node.SuccessorCount > 0)
92+
continue;
5993

60-
deleteVertex(currentVertex); // delete vertex
94+
return node;
6195
}
6296

63-
// vertices all gone; return sortedArray
64-
return _sortedArray;
97+
throw new Exception("Unable to find node without successors: Graph has cycles");
6598
}
6699

67-
#endregion
68-
69-
#region - Private Helper Methods -
70-
71-
// returns vert with no successors (or -1 if no such verts)
72-
private int noSuccessors()
100+
/// <summary>
101+
/// Performs the topological sort and returns an array containing the node keys in a topological order
102+
/// </summary>
103+
/// <returns>Array of node keys</returns>
104+
public int[] Sort()
73105
{
74-
for (int row = 0; row < _numVerts; row++)
75-
{
76-
bool isEdge = false; // edge from row to column in adjMat
77-
for (int col = 0; col < _numVerts; col++)
78-
{
79-
if (_matrix[row, col] > 0) // if edge to another,
80-
{
81-
isEdge = true;
82-
break; // this vertex has a successor try another
83-
}
84-
}
85-
if (!isEdge) // if no edges, has no successors
86-
return row;
87-
}
88-
return -1; // no
89-
}
106+
var sortedArray = new int[_nodes.Length];
90107

91-
private void deleteVertex(int delVert)
92-
{
93-
// if not last vertex, delete from vertexList
94-
if (delVert != _numVerts - 1)
108+
// fill the array back to front as we select nodes without successors.
109+
for (int i = _nodeCount - 1; i >= 0; i--)
95110
{
96-
for (int j = delVert; j < _numVerts - 1; j++)
97-
_vertices[j] = _vertices[j + 1];
98-
99-
for (int row = delVert; row < _numVerts - 1; row++)
100-
moveRowUp(row, _numVerts);
101-
102-
for (int col = delVert; col < _numVerts - 1; col++)
103-
moveColLeft(col, _numVerts - 1);
111+
var node = GetNodeWithoutSuccessors();
112+
sortedArray[i] = node.Index;
113+
EliminateNode(node);
104114
}
105-
_numVerts--; // one less vertex
106-
}
107115

108-
private void moveRowUp(int row, int length)
109-
{
110-
for (int col = 0; col < length; col++)
111-
_matrix[row, col] = _matrix[row + 1, col];
116+
return sortedArray;
112117
}
113-
114-
private void moveColLeft(int col, int length)
115-
{
116-
for (int row = 0; row < length; row++)
117-
_matrix[row, col] = _matrix[row, col + 1];
118-
}
119-
120-
#endregion
121118
}
122119
}

0 commit comments

Comments
 (0)