From 18a196d2704becdce2c3449070d8fd3bf5f678d8 Mon Sep 17 00:00:00 2001 From: Akkerman Date: Thu, 16 Apr 2015 15:09:27 +0200 Subject: [PATCH] - 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) --- src/NHibernate/Loader/JoinWalker.cs | 2 +- src/NHibernate/Loader/TopologicalSorter.cs | 229 +++++++++++---------- 2 files changed, 116 insertions(+), 115 deletions(-) diff --git a/src/NHibernate/Loader/JoinWalker.cs b/src/NHibernate/Loader/JoinWalker.cs index 0427f41dd5c..fabef2fa727 100644 --- a/src/NHibernate/Loader/JoinWalker.cs +++ b/src/NHibernate/Loader/JoinWalker.cs @@ -184,7 +184,7 @@ private static int[] GetTopologicalSortOrder(List 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 diff --git a/src/NHibernate/Loader/TopologicalSorter.cs b/src/NHibernate/Loader/TopologicalSorter.cs index adf13ece1a8..e32796063f7 100644 --- a/src/NHibernate/Loader/TopologicalSorter.cs +++ b/src/NHibernate/Loader/TopologicalSorter.cs @@ -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; + } + + /// + /// Adds a new node + /// + /// index of the new node + 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; + } + + /// + /// Adds an edge from the node with the given sourceNodeIndex to the node with the given destinationNodeIndex + /// + /// index of a previously added node that is the source of the edge + /// index of a previously added node the is the destination of the edge + 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"); + } + + /// + /// Performs the topological sort and returns an array containing the node keys in a topological order + /// + /// Array of node keys + 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; + } + } }