Skip to content

Commit 63888ff

Browse files
AkkermanAkkerman
Akkerman
authored and
Akkerman
committed
- 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 5c739ad commit 63888ff

File tree

2 files changed

+116
-115
lines changed

2 files changed

+116
-115
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

src/NHibernate/Loader/TopologicalSorter.cs

Lines changed: 115 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -3,120 +3,121 @@
33
using System.Linq;
44
using System.Text;
55

6-
// Algorithm from: http://tawani.blogspot.com/2009/02/topological-sorting-and-cyclic.html
76
namespace NHibernate.Loader
87
{
9-
class TopologicalSorter
10-
{
11-
#region - Private Members -
12-
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;
17-
18-
#endregion
19-
20-
#region - CTors -
21-
22-
public TopologicalSorter(int size)
23-
{
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
31-
}
32-
33-
#endregion
34-
35-
#region - Public Methods -
36-
37-
public int AddVertex(int vertex)
38-
{
39-
_vertices[_numVerts++] = vertex;
40-
return _numVerts - 1;
41-
}
42-
43-
public void AddEdge(int start, int end)
44-
{
45-
_matrix[start, end] = 1;
46-
}
47-
48-
public int[] Sort() // toplogical sort
49-
{
50-
while (_numVerts > 0) // while vertices remain,
51-
{
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");
56-
57-
// insert vertex label in sorted array (start at end)
58-
_sortedArray[_numVerts - 1] = _vertices[currentVertex];
59-
60-
deleteVertex(currentVertex); // delete vertex
61-
}
62-
63-
// vertices all gone; return sortedArray
64-
return _sortedArray;
65-
}
66-
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()
73-
{
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-
}
90-
91-
private void deleteVertex(int delVert)
92-
{
93-
// if not last vertex, delete from vertexList
94-
if (delVert != _numVerts - 1)
95-
{
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);
104-
}
105-
_numVerts--; // one less vertex
106-
}
107-
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];
112-
}
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
121-
}
8+
class TopologicalSorter
9+
{
10+
private sealed class Node
11+
{
12+
public int Index { get; private set; }
13+
public int SuccessorCount { get; private set; }
14+
public bool Eliminated { get; private set; }
15+
private System.Action onEliminate;
16+
17+
public Node(int index)
18+
{
19+
Index = index;
20+
SuccessorCount = 0;
21+
Eliminated = false;
22+
onEliminate = null;
23+
}
24+
25+
public void RegisterSuccessor(Node successor)
26+
{
27+
this.SuccessorCount++;
28+
successor.onEliminate += () => this.SuccessorCount--;
29+
}
30+
31+
public void Eliminate()
32+
{
33+
if (onEliminate != null)
34+
{
35+
onEliminate();
36+
onEliminate = null;
37+
}
38+
Eliminated = true;
39+
}
40+
}
41+
42+
private readonly Node[] _nodes;
43+
private int _nodeCount;
44+
45+
public TopologicalSorter(int size)
46+
{
47+
this._nodes = new Node[size];
48+
this._nodeCount = 0;
49+
}
50+
51+
/// <summary>
52+
/// Adds a new node
53+
/// </summary>
54+
/// <returns>index of the new node</returns>
55+
public int AddVertex()
56+
{
57+
// note: this method cannot add nodes beyond the initial size defined in the constructor.
58+
var node = new Node(this._nodeCount++);
59+
this._nodes[node.Index] = node;
60+
return node.Index;
61+
}
62+
63+
/// <summary>
64+
/// Adds an edge from the node with the given sourceNodeIndex to the node with the given destinationNodeIndex
65+
/// </summary>
66+
/// <param name="sourceNodeIndex">index of a previously added node that is the source of the edge</param>
67+
/// <param name="destinationNodeIndex">index of a previously added node the is the destination of the edge</param>
68+
public void AddEdge(int sourceNodeIndex, int destinationNodeIndex)
69+
{
70+
// note: invalid values for "sourceNodeIndex" and "destinationNodeIndex" will either lead to an "IndexOutOfRangeException" or a "NullReferenceException"
71+
_nodes[sourceNodeIndex].RegisterSuccessor(_nodes[destinationNodeIndex]);
72+
}
73+
74+
private void eliminateNode(Node node)
75+
{
76+
node.Eliminate();
77+
78+
// decrease _nodeCount whenever the last nodes are already eliminated.
79+
// This should save time for the following checks in "getNodeWithoutSuccessors".
80+
while (_nodeCount > 0 && _nodes[_nodeCount - 1].Eliminated)
81+
_nodeCount--;
82+
}
83+
84+
private Node getNodeWithoutSuccessors()
85+
{
86+
// note: It would be more performant to check the nodes in reverse order, since that would allow decreases of "_nodeCount" in "eliminateNode"
87+
// as often as possible since high indices are preferred over low indices whenever there is a choice.
88+
// However, several test cases are violated if this change is made; It seems not all restrictions are properly added as edges here.
89+
for (int i = 0; i < _nodeCount; i++)
90+
{
91+
var node = _nodes[i];
92+
if (node.Eliminated)
93+
continue;
94+
95+
if (node.SuccessorCount > 0)
96+
continue;
97+
98+
return node;
99+
}
100+
101+
throw new Exception("Unable to find node without successors: Graph has cycles");
102+
}
103+
104+
/// <summary>
105+
/// Performs the topological sort and returns an array containing the node keys in a topological order
106+
/// </summary>
107+
/// <returns>Array of node keys</returns>
108+
public int[] Sort()
109+
{
110+
var _sortedArray = new int[_nodes.Length];
111+
112+
// fill the array back to front as we select nodes without successors.
113+
for (int i = _nodeCount - 1; i >= 0; i--)
114+
{
115+
var node = this.getNodeWithoutSuccessors();
116+
_sortedArray[i] = node.Index;
117+
this.eliminateNode(node);
118+
}
119+
120+
return _sortedArray;
121+
}
122+
}
122123
}

0 commit comments

Comments
 (0)