Skip to content

Commit

Permalink
- Replaced NHibernate.Loader.TopologicalSorter by a more performant i…
Browse files Browse the repository at this point in the history
…mplementation.

- 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)
  • Loading branch information
Akkerman authored and Akkerman committed Apr 16, 2015
1 parent 091fa6b commit 18a196d
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 115 deletions.
2 changes: 1 addition & 1 deletion src/NHibernate/Loader/JoinWalker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ private static int[] GetTopologicalSortOrder(List<DependentAlias> fields)
// add vertices
for (int i = 0; i < fields.Count; i++)
{
_indexes[fields[i].Alias.ToLower()] = g.AddVertex(i);
_indexes[fields[i].Alias.ToLower()] = g.AddNode();
}

// add edges
Expand Down
229 changes: 115 additions & 114 deletions src/NHibernate/Loader/TopologicalSorter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,120 +3,121 @@
using System.Linq;
using System.Text;

// Algorithm from: http://tawani.blogspot.com/2009/02/topological-sorting-and-cyclic.html
namespace NHibernate.Loader
{
class TopologicalSorter
{
#region - Private Members -

private readonly int[] _vertices; // list of vertices
private readonly int[,] _matrix; // adjacency matrix
private int _numVerts; // current number of vertices
private readonly int[] _sortedArray;

#endregion

#region - CTors -

public TopologicalSorter(int size)
{
_vertices = new int[size];
_matrix = new int[size, size];
_numVerts = 0;
for (int i = 0; i < size; i++)
for (int j = 0; j < size; j++)
_matrix[i, j] = 0;
_sortedArray = new int[size]; // sorted vert labels
}

#endregion

#region - Public Methods -

public int AddVertex(int vertex)
{
_vertices[_numVerts++] = vertex;
return _numVerts - 1;
}

public void AddEdge(int start, int end)
{
_matrix[start, end] = 1;
}

public int[] Sort() // toplogical sort
{
while (_numVerts > 0) // while vertices remain,
{
// get a vertex with no successors, or -1
int currentVertex = noSuccessors();
if (currentVertex == -1) // must be a cycle
throw new Exception("Graph has cycles");

// insert vertex label in sorted array (start at end)
_sortedArray[_numVerts - 1] = _vertices[currentVertex];

deleteVertex(currentVertex); // delete vertex
}

// vertices all gone; return sortedArray
return _sortedArray;
}

#endregion

#region - Private Helper Methods -

// returns vert with no successors (or -1 if no such verts)
private int noSuccessors()
{
for (int row = 0; row < _numVerts; row++)
{
bool isEdge = false; // edge from row to column in adjMat
for (int col = 0; col < _numVerts; col++)
{
if (_matrix[row, col] > 0) // if edge to another,
{
isEdge = true;
break; // this vertex has a successor try another
}
}
if (!isEdge) // if no edges, has no successors
return row;
}
return -1; // no
}

private void deleteVertex(int delVert)
{
// if not last vertex, delete from vertexList
if (delVert != _numVerts - 1)
{
for (int j = delVert; j < _numVerts - 1; j++)
_vertices[j] = _vertices[j + 1];

for (int row = delVert; row < _numVerts - 1; row++)
moveRowUp(row, _numVerts);

for (int col = delVert; col < _numVerts - 1; col++)
moveColLeft(col, _numVerts - 1);
}
_numVerts--; // one less vertex
}

private void moveRowUp(int row, int length)
{
for (int col = 0; col < length; col++)
_matrix[row, col] = _matrix[row + 1, col];
}

private void moveColLeft(int col, int length)
{
for (int row = 0; row < length; row++)
_matrix[row, col] = _matrix[row, col + 1];
}

#endregion
}
class TopologicalSorter
{
private sealed class Node
{
public int Index { get; private set; }
public int SuccessorCount { get; private set; }
public bool Eliminated { get; private set; }
private System.Action onEliminate;

public Node(int index)
{
Index = index;
SuccessorCount = 0;
Eliminated = false;
onEliminate = null;
}

public void RegisterSuccessor(Node successor)
{
this.SuccessorCount++;
successor.onEliminate += () => this.SuccessorCount--;
}

public void Eliminate()
{
if (onEliminate != null)
{
onEliminate();
onEliminate = null;
}
Eliminated = true;
}
}

private readonly Node[] _nodes;
private int _nodeCount;

public TopologicalSorter(int size)
{
this._nodes = new Node[size];
this._nodeCount = 0;
}

/// <summary>
/// Adds a new node
/// </summary>
/// <returns>index of the new node</returns>
public int AddNode()
{
// note: this method cannot add nodes beyond the initial size defined in the constructor.
var node = new Node(this._nodeCount);
this._nodes[this._nodeCount++] = node;
return node.Index;
}

/// <summary>
/// Adds an edge from the node with the given sourceNodeIndex to the node with the given destinationNodeIndex
/// </summary>
/// <param name="sourceNodeIndex">index of a previously added node that is the source of the edge</param>
/// <param name="destinationNodeIndex">index of a previously added node the is the destination of the edge</param>
public void AddEdge(int sourceNodeIndex, int destinationNodeIndex)
{
// note: invalid values for "sourceNodeIndex" and "destinationNodeIndex" will either lead to an "IndexOutOfRangeException" or a "NullReferenceException"
_nodes[sourceNodeIndex].RegisterSuccessor(_nodes[destinationNodeIndex]);
}

private void eliminateNode(Node node)
{
node.Eliminate();

// decrease _nodeCount whenever the last nodes are already eliminated.
// This should save time for the following checks in "getNodeWithoutSuccessors".
while (_nodeCount > 0 && _nodes[_nodeCount - 1].Eliminated)
_nodeCount--;
}

private Node getNodeWithoutSuccessors()
{
// check nodes in reverse order. This is done to allow decreases of "_nodeCount" in "eliminateNode"
// as often as possible since high indices are preferred over low indices whenever there is a choice.
for (int i = _nodeCount - 1; i >= 0; i--)
{
var node = _nodes[i];
if (node.Eliminated)
continue;

if (node.SuccessorCount > 0)
continue;

return node;
}

throw new Exception("Unable to find node without successors: Graph has cycles");
}

/// <summary>
/// Performs the topological sort and returns an array containing the node keys in a topological order
/// </summary>
/// <returns>Array of node keys</returns>
public int[] Sort()
{
var _sortedArray = new int[_nodeCount];

// iteration order is the same as in "getNodeWithoutSuccessors". Doing this will preserve
// the original node order and performance should be good if no edges are present.
for (int i = _nodeCount - 1; i >= 0; i--)
{
var node = this.getNodeWithoutSuccessors();
_sortedArray[i] = node.Index;
this.eliminateNode(node);
}

return _sortedArray;
}
}
}

0 comments on commit 18a196d

Please sign in to comment.