/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
1
# Copyright (C) 2009 Canonical Ltd
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17
"""Implementation of Graph algorithms when we have already loaded everything.
18
"""
19
20
cdef extern from "python-compat.h":
21
    pass
22
23
cdef extern from "Python.h":
24
    ctypedef int Py_ssize_t
25
    ctypedef struct PyObject:
26
        pass
27
4371.3.31 by John Arbash Meinel
Change _KnownGraph.parents to be a tuple rather than a list.
28
    object PyTuple_New(Py_ssize_t n)
4371.4.23 by John Arbash Meinel
Clean up the imports at the top.
29
    Py_ssize_t PyTuple_GET_SIZE(object t)
30
    PyObject * PyTuple_GET_ITEM(object t, Py_ssize_t o)
4371.3.31 by John Arbash Meinel
Change _KnownGraph.parents to be a tuple rather than a list.
31
    void PyTuple_SET_ITEM(object t, Py_ssize_t o, object v)
4371.4.23 by John Arbash Meinel
Clean up the imports at the top.
32
33
    Py_ssize_t PyList_GET_SIZE(object l)
34
    PyObject * PyList_GET_ITEM(object l, Py_ssize_t o)
35
    int PyList_SetItem(object l, Py_ssize_t o, object l) except -1
36
    int PyList_Append(object l, object v) except -1
37
38
    int PyDict_CheckExact(object d)
39
    Py_ssize_t PyDict_Size(object d) except -1
4371.3.28 by John Arbash Meinel
Finish converting away from 'cdef list' syntax.
40
    PyObject * PyDict_GetItem(object d, object k)
4371.4.23 by John Arbash Meinel
Clean up the imports at the top.
41
    int PyDict_SetItem(object d, object k, object v) except -1
4371.4.18 by John Arbash Meinel
Big performance win, back to 650ms.
42
    int PyDict_DelItem(object d, object k) except -1
4371.3.28 by John Arbash Meinel
Finish converting away from 'cdef list' syntax.
43
    int PyDict_Next(object d, Py_ssize_t *pos, PyObject **k, PyObject **v)
4371.4.21 by John Arbash Meinel
Spend some time optimizing the inner heads() loop, approx 2x faster.
44
4371.3.20 by John Arbash Meinel
A few tweaks to the pyrex version.
45
    void Py_INCREF(object)
46
4593.5.15 by John Arbash Meinel
Use the same implementation of stack handling to avoid append/pop.
47
import gc
4371.3.21 by John Arbash Meinel
Attempt to make things better by splitting out left_parent and right_parent into
48
4593.5.3 by John Arbash Meinel
Bring in the optimized tsort code.
49
from bzrlib import errors, revision
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
50
4371.4.21 by John Arbash Meinel
Spend some time optimizing the inner heads() loop, approx 2x faster.
51
cdef object NULL_REVISION
52
NULL_REVISION = revision.NULL_REVISION
53
54
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
55
cdef class _KnownGraphNode:
56
    """Represents a single object in the known graph."""
57
4371.3.35 by John Arbash Meinel
Changing the heapq algorithm actually ends up with better overall performance.
58
    cdef object key
4371.3.28 by John Arbash Meinel
Finish converting away from 'cdef list' syntax.
59
    cdef object parents
60
    cdef object children
4371.4.13 by Vincent Ladeuil
Fixed as per John's review.
61
    cdef public long gdfo
4371.4.22 by John Arbash Meinel
Changing from a set of seen to a list to cleanup and a seen attribute.
62
    cdef int seen
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
63
    cdef object extra
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
64
4371.3.31 by John Arbash Meinel
Change _KnownGraph.parents to be a tuple rather than a list.
65
    def __init__(self, key):
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
66
        self.key = key
4371.3.31 by John Arbash Meinel
Change _KnownGraph.parents to be a tuple rather than a list.
67
        self.parents = None
4371.3.21 by John Arbash Meinel
Attempt to make things better by splitting out left_parent and right_parent into
68
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
69
        self.children = []
70
        # Greatest distance from origin
71
        self.gdfo = -1
4371.4.22 by John Arbash Meinel
Changing from a set of seen to a list to cleanup and a seen attribute.
72
        self.seen = 0
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
73
        self.extra = None
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
74
75
    property child_keys:
76
        def __get__(self):
77
            cdef _KnownGraphNode child
78
79
            keys = []
80
            for child in self.children:
4371.3.28 by John Arbash Meinel
Finish converting away from 'cdef list' syntax.
81
                PyList_Append(keys, child.key)
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
82
            return keys
83
84
    cdef clear_references(self):
85
        self.parents = None
86
        self.children = None
87
88
    def __repr__(self):
4371.3.35 by John Arbash Meinel
Changing the heapq algorithm actually ends up with better overall performance.
89
        cdef _KnownGraphNode node
90
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
91
        parent_keys = []
4371.3.35 by John Arbash Meinel
Changing the heapq algorithm actually ends up with better overall performance.
92
        if self.parents is not None:
93
            for node in self.parents:
94
                parent_keys.append(node.key)
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
95
        child_keys = []
4371.3.35 by John Arbash Meinel
Changing the heapq algorithm actually ends up with better overall performance.
96
        if self.children is not None:
97
            for node in self.children:
98
                child_keys.append(node.key)
4371.4.10 by Vincent Ladeuil
Cleanup.
99
        return '%s(%s  gdfo:%s par:%s child:%s)' % (
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
100
            self.__class__.__name__, self.key, self.gdfo,
4371.4.12 by Vincent Ladeuil
Fix typos.
101
            parent_keys, child_keys)
4371.4.10 by Vincent Ladeuil
Cleanup.
102
4371.3.37 by John Arbash Meinel
cleanup passes.
103
4371.4.14 by John Arbash Meinel
Optimize _find_gdfo and make known_parent_gdfos a member of KnownGraphNode
104
cdef _KnownGraphNode _get_list_node(lst, Py_ssize_t pos):
105
    cdef PyObject *temp_node
106
107
    temp_node = PyList_GET_ITEM(lst, pos)
108
    return <_KnownGraphNode>temp_node
109
110
111
cdef _KnownGraphNode _get_parent(parents, Py_ssize_t pos):
112
    cdef PyObject *temp_node
113
    cdef _KnownGraphNode node
114
115
    temp_node = PyTuple_GET_ITEM(parents, pos)
116
    return <_KnownGraphNode>temp_node
117
118
4593.5.8 by John Arbash Meinel
Approximate implementation in Pyrex.
119
cdef class _MergeSorter
120
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
121
cdef class KnownGraph:
122
    """This is a class which assumes we already know the full graph."""
123
124
    cdef public object _nodes
4371.3.26 by John Arbash Meinel
A few more cleanups. Start moving away from pyrex 0.9.8isms
125
    cdef object _known_heads
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
126
    cdef public int do_cache
127
128
    def __init__(self, parent_map, do_cache=True):
129
        """Create a new KnownGraph instance.
130
131
        :param parent_map: A dictionary mapping key => parent_keys
132
        """
4371.4.29 by John Arbash Meinel
Pre-allocating self._nodes turned out to be slower.
133
        # tests at pre-allocating the node dict actually slowed things down
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
134
        self._nodes = {}
135
        # Maps {sorted(revision_id, revision_id): heads}
136
        self._known_heads = {}
137
        self.do_cache = int(do_cache)
4593.5.29 by John Arbash Meinel
Remove the gc enable/disable code.
138
        # TODO: consider disabling gc since we are allocating a lot of nodes
139
        #       that won't be collectable anyway. real world testing has not
140
        #       shown a specific impact, yet.
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
141
        self._initialize_nodes(parent_map)
142
        self._find_gdfo()
143
144
    def __dealloc__(self):
145
        cdef _KnownGraphNode child
4371.3.28 by John Arbash Meinel
Finish converting away from 'cdef list' syntax.
146
        cdef Py_ssize_t pos
147
        cdef PyObject *temp_node
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
148
4371.3.28 by John Arbash Meinel
Finish converting away from 'cdef list' syntax.
149
        while PyDict_Next(self._nodes, &pos, NULL, &temp_node):
150
            child = <_KnownGraphNode>temp_node
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
151
            child.clear_references()
152
4371.4.28 by John Arbash Meinel
We spent a lot of time adding and removing keys from tails.
153
    cdef _KnownGraphNode _get_or_create_node(self, key):
4371.3.31 by John Arbash Meinel
Change _KnownGraph.parents to be a tuple rather than a list.
154
        cdef PyObject *temp_node
155
        cdef _KnownGraphNode node
156
157
        temp_node = PyDict_GetItem(self._nodes, key)
158
        if temp_node == NULL:
159
            node = _KnownGraphNode(key)
160
            PyDict_SetItem(self._nodes, key, node)
161
        else:
162
            node = <_KnownGraphNode>temp_node
163
        return node
164
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
165
    def _initialize_nodes(self, parent_map):
166
        """Populate self._nodes.
167
4371.4.9 by Vincent Ladeuil
Simplify gdfo computing by finding tails when at graph build time.
168
        After this has finished:
169
        - self._nodes will have an entry for every entry in parent_map.
170
        - ghosts will have a parent_keys = None,
4371.4.12 by Vincent Ladeuil
Fix typos.
171
        - all nodes found will also have child_keys populated with all known
172
          child keys,
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
173
        """
4371.3.28 by John Arbash Meinel
Finish converting away from 'cdef list' syntax.
174
        cdef PyObject *temp_key, *temp_parent_keys, *temp_node
4371.3.31 by John Arbash Meinel
Change _KnownGraph.parents to be a tuple rather than a list.
175
        cdef Py_ssize_t pos, pos2, num_parent_keys
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
176
        cdef _KnownGraphNode node
177
        cdef _KnownGraphNode parent_node
178
4371.3.28 by John Arbash Meinel
Finish converting away from 'cdef list' syntax.
179
        if not PyDict_CheckExact(parent_map):
180
            raise TypeError('parent_map should be a dict of {key:parent_keys}')
181
        # for key, parent_keys in parent_map.iteritems():
182
        pos = 0
183
        while PyDict_Next(parent_map, &pos, &temp_key, &temp_parent_keys):
184
            key = <object>temp_key
185
            parent_keys = <object>temp_parent_keys
4371.4.9 by Vincent Ladeuil
Simplify gdfo computing by finding tails when at graph build time.
186
            num_parent_keys = len(parent_keys)
4371.4.28 by John Arbash Meinel
We spent a lot of time adding and removing keys from tails.
187
            node = self._get_or_create_node(key)
4593.5.26 by John Arbash Meinel
small cleanup, a bit of comment tweaking.
188
            # We know how many parents, so we pre allocate the tuple
4371.3.31 by John Arbash Meinel
Change _KnownGraph.parents to be a tuple rather than a list.
189
            parent_nodes = PyTuple_New(num_parent_keys)
190
            for pos2 from 0 <= pos2 < num_parent_keys:
4593.5.26 by John Arbash Meinel
small cleanup, a bit of comment tweaking.
191
                # Note: it costs us 10ms out of 40ms to lookup all of these
192
                #       parents, it doesn't seem to be an allocation overhead,
193
                #       but rather a lookup overhead. There doesn't seem to be
194
                #       a way around it, and that is one reason why
195
                #       KnownGraphNode maintains a direct pointer to the parent
196
                #       node.
197
                # We use [] because parent_keys may be a tuple or list
4371.4.28 by John Arbash Meinel
We spent a lot of time adding and removing keys from tails.
198
                parent_node = self._get_or_create_node(parent_keys[pos2])
4371.3.31 by John Arbash Meinel
Change _KnownGraph.parents to be a tuple rather than a list.
199
                # PyTuple_SET_ITEM will steal a reference, so INCREF first
200
                Py_INCREF(parent_node)
201
                PyTuple_SET_ITEM(parent_nodes, pos2, parent_node)
4371.3.28 by John Arbash Meinel
Finish converting away from 'cdef list' syntax.
202
                PyList_Append(parent_node.children, node)
203
            node.parents = parent_nodes
4371.4.30 by John Arbash Meinel
We don't need self._tails anymore, nor does it need to be a set.
204
205
    def _find_tails(self):
206
        cdef PyObject *temp_node
207
        cdef _KnownGraphNode node
208
        cdef Py_ssize_t pos
209
210
        tails = []
4371.4.28 by John Arbash Meinel
We spent a lot of time adding and removing keys from tails.
211
        pos = 0
212
        while PyDict_Next(self._nodes, &pos, NULL, &temp_node):
213
            node = <_KnownGraphNode>temp_node
214
            if node.parents is None or PyTuple_GET_SIZE(node.parents) == 0:
4371.4.30 by John Arbash Meinel
We don't need self._tails anymore, nor does it need to be a set.
215
                node.gdfo = 1
216
                PyList_Append(tails, node)
217
        return tails
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
218
219
    def _find_gdfo(self):
4371.4.8 by Vincent Ladeuil
Replace the existing KnownGraph._find_gdfo.
220
        cdef _KnownGraphNode node
221
        cdef _KnownGraphNode child
4371.4.14 by John Arbash Meinel
Optimize _find_gdfo and make known_parent_gdfos a member of KnownGraphNode
222
        cdef PyObject *temp
4371.4.30 by John Arbash Meinel
We don't need self._tails anymore, nor does it need to be a set.
223
        cdef Py_ssize_t pos
4371.4.14 by John Arbash Meinel
Optimize _find_gdfo and make known_parent_gdfos a member of KnownGraphNode
224
        cdef int replace
4371.4.26 by John Arbash Meinel
Don't shrink the pending list.
225
        cdef Py_ssize_t last_item
4371.4.25 by John Arbash Meinel
We had an 'child_node.gdfo is None' check in the inner loop.
226
        cdef long next_gdfo
4371.4.8 by Vincent Ladeuil
Replace the existing KnownGraph._find_gdfo.
227
4371.4.30 by John Arbash Meinel
We don't need self._tails anymore, nor does it need to be a set.
228
        pending = self._find_tails()
4371.4.8 by Vincent Ladeuil
Replace the existing KnownGraph._find_gdfo.
229
4371.4.26 by John Arbash Meinel
Don't shrink the pending list.
230
        last_item = PyList_GET_SIZE(pending) - 1
231
        while last_item >= 0:
4371.4.14 by John Arbash Meinel
Optimize _find_gdfo and make known_parent_gdfos a member of KnownGraphNode
232
            # Avoid pop followed by push, instead, peek, and replace
4371.4.16 by John Arbash Meinel
Document some perf issues with using known_parent_gdfos, but leave it as a dict.
233
            # timing shows this is 930ms => 770ms for OOo
4371.4.26 by John Arbash Meinel
Don't shrink the pending list.
234
            node = _get_list_node(pending, last_item)
235
            last_item = last_item - 1
4371.4.25 by John Arbash Meinel
We had an 'child_node.gdfo is None' check in the inner loop.
236
            next_gdfo = node.gdfo + 1
4371.4.30 by John Arbash Meinel
We don't need self._tails anymore, nor does it need to be a set.
237
            for pos from 0 <= pos < PyList_GET_SIZE(node.children):
238
                child = _get_list_node(node.children, pos)
4371.4.25 by John Arbash Meinel
We had an 'child_node.gdfo is None' check in the inner loop.
239
                if next_gdfo > child.gdfo:
240
                    child.gdfo = next_gdfo
4371.4.27 by John Arbash Meinel
Re-use the child.seen attribute for tracking num parents.
241
                child.seen = child.seen + 1
242
                if child.seen == PyTuple_GET_SIZE(child.parents):
4371.4.17 by John Arbash Meinel
tried a different tack, to use a new list and append.
243
                    # This child is populated, queue it to be walked
4371.4.26 by John Arbash Meinel
Don't shrink the pending list.
244
                    last_item = last_item + 1
245
                    if last_item < PyList_GET_SIZE(pending):
4371.4.14 by John Arbash Meinel
Optimize _find_gdfo and make known_parent_gdfos a member of KnownGraphNode
246
                        Py_INCREF(child) # SetItem steals a ref
4371.4.26 by John Arbash Meinel
Don't shrink the pending list.
247
                        PyList_SetItem(pending, last_item, child)
4371.4.14 by John Arbash Meinel
Optimize _find_gdfo and make known_parent_gdfos a member of KnownGraphNode
248
                    else:
249
                        PyList_Append(pending, child)
4371.4.27 by John Arbash Meinel
Re-use the child.seen attribute for tracking num parents.
250
                    # We have queued this node, we don't need to track it
251
                    # anymore
252
                    child.seen = 0
4371.4.8 by Vincent Ladeuil
Replace the existing KnownGraph._find_gdfo.
253
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
254
    def heads(self, keys):
255
        """Return the heads from amongst keys.
256
257
        This is done by searching the ancestries of each key.  Any key that is
258
        reachable from another key is not returned; all the others are.
259
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
260
        This operation scales with the relative depth between any two keys. It
261
        uses gdfo to avoid walking all ancestry.
262
263
        :param keys: An iterable of keys.
264
        :return: A set of the heads. Note that as a set there is no ordering
265
            information. Callers will need to filter their input to create
266
            order if they need it.
267
        """
268
        cdef PyObject *maybe_node
269
        cdef PyObject *maybe_heads
270
        cdef PyObject *temp_node
271
        cdef _KnownGraphNode node
4371.4.31 by John Arbash Meinel
Apply the same 'never pop' logic to the heads() code.
272
        cdef Py_ssize_t pos, last_item
4371.4.21 by John Arbash Meinel
Spend some time optimizing the inner heads() loop, approx 2x faster.
273
        cdef long min_gdfo
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
274
4526.10.1 by John Arbash Meinel
Avoid using PyFrozenSet_New
275
        heads_key = frozenset(keys)
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
276
        maybe_heads = PyDict_GetItem(self._known_heads, heads_key)
277
        if maybe_heads != NULL:
278
            return <object>maybe_heads
279
        # Not cached, compute it ourselves
280
        candidate_nodes = {}
281
        for key in keys:
4371.4.28 by John Arbash Meinel
We spent a lot of time adding and removing keys from tails.
282
            maybe_node = PyDict_GetItem(self._nodes, key)
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
283
            if maybe_node == NULL:
284
                raise KeyError('key %s not in nodes' % (key,))
285
            PyDict_SetItem(candidate_nodes, key, <object>maybe_node)
4371.4.21 by John Arbash Meinel
Spend some time optimizing the inner heads() loop, approx 2x faster.
286
        maybe_node = PyDict_GetItem(candidate_nodes, NULL_REVISION)
287
        if maybe_node != NULL:
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
288
            # NULL_REVISION is only a head if it is the only entry
4371.4.21 by John Arbash Meinel
Spend some time optimizing the inner heads() loop, approx 2x faster.
289
            candidate_nodes.pop(NULL_REVISION)
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
290
            if not candidate_nodes:
4371.4.21 by John Arbash Meinel
Spend some time optimizing the inner heads() loop, approx 2x faster.
291
                return frozenset([NULL_REVISION])
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
292
            # The keys changed, so recalculate heads_key
4526.10.1 by John Arbash Meinel
Avoid using PyFrozenSet_New
293
            heads_key = frozenset(candidate_nodes)
4371.4.21 by John Arbash Meinel
Spend some time optimizing the inner heads() loop, approx 2x faster.
294
        if PyDict_Size(candidate_nodes) < 2:
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
295
            return heads_key
296
4371.4.22 by John Arbash Meinel
Changing from a set of seen to a list to cleanup and a seen attribute.
297
        cleanup = []
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
298
        pending = []
4371.4.21 by John Arbash Meinel
Spend some time optimizing the inner heads() loop, approx 2x faster.
299
        # we know a gdfo cannot be longer than a linear chain of all nodes
300
        min_gdfo = PyDict_Size(self._nodes) + 1
301
        # Build up nodes that need to be walked, note that starting nodes are
302
        # not added to seen()
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
303
        pos = 0
304
        while PyDict_Next(candidate_nodes, &pos, NULL, &temp_node):
305
            node = <_KnownGraphNode>temp_node
306
            if node.parents is not None:
307
                pending.extend(node.parents)
4371.4.21 by John Arbash Meinel
Spend some time optimizing the inner heads() loop, approx 2x faster.
308
            if node.gdfo < min_gdfo:
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
309
                min_gdfo = node.gdfo
4371.4.21 by John Arbash Meinel
Spend some time optimizing the inner heads() loop, approx 2x faster.
310
311
        # Now do all the real work
4371.4.31 by John Arbash Meinel
Apply the same 'never pop' logic to the heads() code.
312
        last_item = PyList_GET_SIZE(pending) - 1
313
        while last_item >= 0:
314
            node = _get_list_node(pending, last_item)
315
            last_item = last_item - 1
4371.4.22 by John Arbash Meinel
Changing from a set of seen to a list to cleanup and a seen attribute.
316
            if node.seen:
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
317
                # node already appears in some ancestry
318
                continue
4371.4.22 by John Arbash Meinel
Changing from a set of seen to a list to cleanup and a seen attribute.
319
            PyList_Append(cleanup, node)
320
            node.seen = 1
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
321
            if node.gdfo <= min_gdfo:
322
                continue
4371.4.21 by John Arbash Meinel
Spend some time optimizing the inner heads() loop, approx 2x faster.
323
            if node.parents is not None and PyTuple_GET_SIZE(node.parents) > 0:
324
                for pos from 0 <= pos < PyTuple_GET_SIZE(node.parents):
325
                    parent_node = _get_parent(node.parents, pos)
4371.4.31 by John Arbash Meinel
Apply the same 'never pop' logic to the heads() code.
326
                    last_item = last_item + 1
327
                    if last_item < PyList_GET_SIZE(pending):
328
                        Py_INCREF(parent_node) # SetItem steals a ref
329
                        PyList_SetItem(pending, last_item, parent_node)
4371.4.21 by John Arbash Meinel
Spend some time optimizing the inner heads() loop, approx 2x faster.
330
                    else:
331
                        PyList_Append(pending, parent_node)
4371.4.22 by John Arbash Meinel
Changing from a set of seen to a list to cleanup and a seen attribute.
332
        heads = []
333
        pos = 0
334
        while PyDict_Next(candidate_nodes, &pos, NULL, &temp_node):
335
            node = <_KnownGraphNode>temp_node
336
            if not node.seen:
337
                PyList_Append(heads, node.key)
4526.10.1 by John Arbash Meinel
Avoid using PyFrozenSet_New
338
        heads = frozenset(heads)
4371.4.22 by John Arbash Meinel
Changing from a set of seen to a list to cleanup and a seen attribute.
339
        for pos from 0 <= pos < PyList_GET_SIZE(cleanup):
340
            node = _get_list_node(cleanup, pos)
341
            node.seen = 0
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
342
        if self.do_cache:
4371.4.21 by John Arbash Meinel
Spend some time optimizing the inner heads() loop, approx 2x faster.
343
            PyDict_SetItem(self._known_heads, heads_key, heads)
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
344
        return heads
4577.3.2 by John Arbash Meinel
Implement KnownGraph.topo_sort.
345
346
    def topo_sort(self):
347
        """Return the nodes in topological order.
348
349
        All parents must occur before all children.
350
        """
351
        # This is, for the most part, the same iteration order that we used for
352
        # _find_gdfo, consider finding a way to remove the duplication
353
        # In general, we find the 'tails' (nodes with no parents), and then
354
        # walk to the children. For children that have all of their parents
355
        # yielded, we queue up the child to be yielded as well.
356
        cdef _KnownGraphNode node
357
        cdef _KnownGraphNode child
358
        cdef PyObject *temp
359
        cdef Py_ssize_t pos
360
        cdef int replace
361
        cdef Py_ssize_t last_item
362
363
        pending = self._find_tails()
4593.5.3 by John Arbash Meinel
Bring in the optimized tsort code.
364
        if PyList_GET_SIZE(pending) == 0 and len(self._nodes) > 0:
365
            raise errors.GraphCycleError(self._nodes)
4577.3.2 by John Arbash Meinel
Implement KnownGraph.topo_sort.
366
367
        topo_order = []
368
369
        last_item = PyList_GET_SIZE(pending) - 1
370
        while last_item >= 0:
371
            # Avoid pop followed by push, instead, peek, and replace
372
            # timing shows this is 930ms => 770ms for OOo
373
            node = _get_list_node(pending, last_item)
374
            last_item = last_item - 1
4593.5.3 by John Arbash Meinel
Bring in the optimized tsort code.
375
            if node.parents is not None:
376
                # We don't include ghost parents
377
                PyList_Append(topo_order, node.key)
4577.3.2 by John Arbash Meinel
Implement KnownGraph.topo_sort.
378
            for pos from 0 <= pos < PyList_GET_SIZE(node.children):
379
                child = _get_list_node(node.children, pos)
4593.5.3 by John Arbash Meinel
Bring in the optimized tsort code.
380
                if child.gdfo == -1:
381
                    # We know we have a graph cycle because a node has a parent
382
                    # which we couldn't find
383
                    raise errors.GraphCycleError(self._nodes)
4577.3.2 by John Arbash Meinel
Implement KnownGraph.topo_sort.
384
                child.seen = child.seen + 1
385
                if child.seen == PyTuple_GET_SIZE(child.parents):
386
                    # All parents of this child have been yielded, queue this
387
                    # one to be yielded as well
388
                    last_item = last_item + 1
389
                    if last_item < PyList_GET_SIZE(pending):
390
                        Py_INCREF(child) # SetItem steals a ref
391
                        PyList_SetItem(pending, last_item, child)
392
                    else:
393
                        PyList_Append(pending, child)
394
                    # We have queued this node, we don't need to track it
395
                    # anymore
396
                    child.seen = 0
397
        # We started from the parents, so we don't need to do anymore work
398
        return topo_order
4593.5.6 by John Arbash Meinel
Create the crude merge_sort implementation that just thunks over to the old implementation.
399
400
401
    def merge_sort(self, tip_key):
402
        """Compute the merge sorted graph output."""
4593.5.8 by John Arbash Meinel
Approximate implementation in Pyrex.
403
        cdef _MergeSorter sorter
404
4593.5.29 by John Arbash Meinel
Remove the gc enable/disable code.
405
        # TODO: consider disabling gc since we are allocating a lot of nodes
406
        #       that won't be collectable anyway. real world testing has not
407
        #       shown a specific impact, yet.
4593.5.8 by John Arbash Meinel
Approximate implementation in Pyrex.
408
        sorter = _MergeSorter(self, tip_key)
409
        return sorter.topo_order()
4593.5.7 by John Arbash Meinel
start working on a pyrex implementation of MergeSorter
410
411
412
cdef class _MergeSortNode:
413
    """Tracks information about a node during the merge_sort operation."""
414
4593.5.34 by John Arbash Meinel
Change the KnownGraph.merge_sort api.
415
    # Public api
416
    cdef public object key
417
    cdef public long merge_depth
418
    cdef public object end_of_merge # True/False Is this the end of the current merge
419
420
    # Private api, used while computing the information
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
421
    cdef _KnownGraphNode left_parent
422
    cdef _KnownGraphNode left_pending_parent
423
    cdef object pending_parents # list of _KnownGraphNode for non-left parents
4593.5.34 by John Arbash Meinel
Change the KnownGraph.merge_sort api.
424
    cdef long _revno_first
425
    cdef long _revno_second
426
    cdef long _revno_last
4593.5.12 by John Arbash Meinel
Move some sets/dicts into attributes.
427
    # TODO: turn these into flag/bit fields rather than individual members
4593.5.7 by John Arbash Meinel
start working on a pyrex implementation of MergeSorter
428
    cdef int is_first_child # Is this the first child?
4593.5.12 by John Arbash Meinel
Move some sets/dicts into attributes.
429
    cdef int seen_by_child # A child node has seen this parent
430
    cdef int completed # Fully Processed
431
4593.5.34 by John Arbash Meinel
Change the KnownGraph.merge_sort api.
432
    def __init__(self, key):
433
        self.key = key
4593.5.12 by John Arbash Meinel
Move some sets/dicts into attributes.
434
        self.merge_depth = -1
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
435
        self.left_parent = None
4593.5.12 by John Arbash Meinel
Move some sets/dicts into attributes.
436
        self.left_pending_parent = None
437
        self.pending_parents = None
4593.5.34 by John Arbash Meinel
Change the KnownGraph.merge_sort api.
438
        self._revno_first = -1
439
        self._revno_second = -1
440
        self._revno_last = -1
4593.5.12 by John Arbash Meinel
Move some sets/dicts into attributes.
441
        self.is_first_child = 0
442
        self.seen_by_child = 0
443
        self.completed = 0
4593.5.8 by John Arbash Meinel
Approximate implementation in Pyrex.
444
445
    def __repr__(self):
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
446
        return '%s(depth:%s rev:%s,%s,%s first:%s seen:%s)' % (self.__class__.__name__,
447
            self.merge_depth,
4593.5.34 by John Arbash Meinel
Change the KnownGraph.merge_sort api.
448
            self._revno_first, self._revno_second, self._revno_last,
4593.5.12 by John Arbash Meinel
Move some sets/dicts into attributes.
449
            self.is_first_child, self.seen_by_child)
450
451
    cdef int has_pending_parents(self):
452
        if self.left_pending_parent is not None or self.pending_parents:
453
            return 1
454
        return 0
4593.5.7 by John Arbash Meinel
start working on a pyrex implementation of MergeSorter
455
4593.5.34 by John Arbash Meinel
Change the KnownGraph.merge_sort api.
456
    cdef object _revno(self):
457
        if self._revno_first == -1:
458
            if self._revno_second != -1:
459
                raise RuntimeError('Something wrong with: %s' % (self,))
460
            return (self._revno_last,)
461
        else:
462
            return (self._revno_first, self._revno_second, self._revno_last)
463
464
    property revno:
465
        def __get__(self):
466
            return self._revno()
467
4593.5.7 by John Arbash Meinel
start working on a pyrex implementation of MergeSorter
468
469
cdef class _MergeSorter:
470
    """This class does the work of computing the merge_sort ordering.
471
472
    We have some small advantages, in that we get all the extra information
473
    that KnownGraph knows, like knowing the child lists, etc.
474
    """
475
4593.5.11 by John Arbash Meinel
Without doing any real tuning yet, we see a decent speedup for merge_sort:
476
    # Current performance numbers for merge_sort(bzr_dev_parent_map):
4593.5.25 by John Arbash Meinel
Add tests that we detect GraphCycleError
477
    #  302ms tsort.merge_sort()
478
    #   91ms graph.KnownGraph().merge_sort()
4593.5.28 by John Arbash Meinel
Cleanup pass
479
    #   40ms kg.merge_sort()
4593.5.11 by John Arbash Meinel
Without doing any real tuning yet, we see a decent speedup for merge_sort:
480
4593.5.7 by John Arbash Meinel
start working on a pyrex implementation of MergeSorter
481
    cdef KnownGraph graph
4593.5.15 by John Arbash Meinel
Use the same implementation of stack handling to avoid append/pop.
482
    cdef object _depth_first_stack  # list
483
    cdef Py_ssize_t _last_stack_item # offset to last item on stack
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
484
    # cdef object _ms_nodes # dict of key => _MergeSortNode
4593.5.8 by John Arbash Meinel
Approximate implementation in Pyrex.
485
    cdef object _revno_to_branch_count # {revno => num child branches}
486
    cdef object _scheduled_nodes # List of nodes ready to be yielded
4593.5.7 by John Arbash Meinel
start working on a pyrex implementation of MergeSorter
487
488
    def __init__(self, known_graph, tip_key):
4593.5.12 by John Arbash Meinel
Move some sets/dicts into attributes.
489
        cdef _KnownGraphNode node
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
490
4593.5.7 by John Arbash Meinel
start working on a pyrex implementation of MergeSorter
491
        self.graph = known_graph
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
492
        # self._ms_nodes = {}
4593.5.8 by John Arbash Meinel
Approximate implementation in Pyrex.
493
        self._revno_to_branch_count = {}
4593.5.15 by John Arbash Meinel
Use the same implementation of stack handling to avoid append/pop.
494
        self._depth_first_stack = []
495
        self._last_stack_item = -1
4593.5.8 by John Arbash Meinel
Approximate implementation in Pyrex.
496
        self._scheduled_nodes = []
4593.5.42 by John Arbash Meinel
Turns out that some code assumed passing NULL_REVISION to merge_sort
497
        if (tip_key is not None and tip_key != NULL_REVISION
498
            and tip_key != (NULL_REVISION,)):
4593.5.7 by John Arbash Meinel
start working on a pyrex implementation of MergeSorter
499
            node = self.graph._nodes[tip_key]
4593.5.27 by John Arbash Meinel
bit of cleanup passes
500
            self._get_ms_node(node)
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
501
            self._push_node(node, 0)
4593.5.12 by John Arbash Meinel
Move some sets/dicts into attributes.
502
4593.5.27 by John Arbash Meinel
bit of cleanup passes
503
    cdef _MergeSortNode _get_ms_node(self, _KnownGraphNode node):
4593.5.12 by John Arbash Meinel
Move some sets/dicts into attributes.
504
        cdef PyObject *temp_node
505
        cdef _MergeSortNode ms_node
506
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
507
        if node.extra is None:
4593.5.34 by John Arbash Meinel
Change the KnownGraph.merge_sort api.
508
            ms_node = _MergeSortNode(node.key)
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
509
            node.extra = ms_node
4593.5.12 by John Arbash Meinel
Move some sets/dicts into attributes.
510
        else:
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
511
            ms_node = <_MergeSortNode>node.extra
4593.5.12 by John Arbash Meinel
Move some sets/dicts into attributes.
512
        return ms_node
513
4593.5.24 by John Arbash Meinel
Switch some variables from Py_ssize_t to long
514
    cdef _push_node(self, _KnownGraphNode node, long merge_depth):
4593.5.7 by John Arbash Meinel
start working on a pyrex implementation of MergeSorter
515
        cdef _KnownGraphNode parent_node
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
516
        cdef _MergeSortNode ms_node, ms_parent_node
4593.5.12 by John Arbash Meinel
Move some sets/dicts into attributes.
517
        cdef Py_ssize_t pos
4593.5.7 by John Arbash Meinel
start working on a pyrex implementation of MergeSorter
518
4593.5.27 by John Arbash Meinel
bit of cleanup passes
519
        ms_node = self._get_ms_node(node)
4593.5.7 by John Arbash Meinel
start working on a pyrex implementation of MergeSorter
520
        ms_node.merge_depth = merge_depth
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
521
        if PyTuple_GET_SIZE(node.parents) > 0:
522
            parent_node = _get_parent(node.parents, 0)
523
            ms_node.left_parent = parent_node
524
            ms_node.left_pending_parent = parent_node
525
        if PyTuple_GET_SIZE(node.parents) > 1:
4593.5.20 by John Arbash Meinel
Expose KnownGraph off of VersionedFiles
526
            ms_node.pending_parents = []
527
            for pos from 1 <= pos < PyTuple_GET_SIZE(node.parents):
528
                parent_node = _get_parent(node.parents, pos)
529
                if parent_node.parents is None: # ghost
530
                    continue
531
                PyList_Append(ms_node.pending_parents, parent_node)
4593.5.12 by John Arbash Meinel
Move some sets/dicts into attributes.
532
4593.5.7 by John Arbash Meinel
start working on a pyrex implementation of MergeSorter
533
        ms_node.is_first_child = 1
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
534
        if ms_node.left_parent is not None:
4593.5.27 by John Arbash Meinel
bit of cleanup passes
535
            ms_parent_node = self._get_ms_node(ms_node.left_parent)
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
536
            if ms_parent_node.seen_by_child:
4593.5.8 by John Arbash Meinel
Approximate implementation in Pyrex.
537
                ms_node.is_first_child = 0
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
538
            ms_parent_node.seen_by_child = 1
4593.5.15 by John Arbash Meinel
Use the same implementation of stack handling to avoid append/pop.
539
        self._last_stack_item = self._last_stack_item + 1
540
        if self._last_stack_item < PyList_GET_SIZE(self._depth_first_stack):
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
541
            Py_INCREF(node) # SetItem steals a ref
4593.5.15 by John Arbash Meinel
Use the same implementation of stack handling to avoid append/pop.
542
            PyList_SetItem(self._depth_first_stack, self._last_stack_item,
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
543
                           node)
4593.5.15 by John Arbash Meinel
Use the same implementation of stack handling to avoid append/pop.
544
        else:
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
545
            PyList_Append(self._depth_first_stack, node)
4593.5.8 by John Arbash Meinel
Approximate implementation in Pyrex.
546
547
    cdef _pop_node(self):
4593.5.16 by John Arbash Meinel
Change the revno handling code.
548
        cdef PyObject *temp
4593.5.23 by John Arbash Meinel
Move the end-of-merge computation into _pop_node
549
        cdef _MergeSortNode ms_node, ms_parent_node, ms_prev_node
550
        cdef _KnownGraphNode node, parent_node, prev_node
4593.5.8 by John Arbash Meinel
Approximate implementation in Pyrex.
551
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
552
        node = _get_list_node(self._depth_first_stack, self._last_stack_item)
553
        ms_node = <_MergeSortNode>node.extra
4593.5.15 by John Arbash Meinel
Use the same implementation of stack handling to avoid append/pop.
554
        self._last_stack_item = self._last_stack_item - 1
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
555
        if ms_node.left_parent is not None:
4593.5.27 by John Arbash Meinel
bit of cleanup passes
556
            # Assign the revision number from the left-hand parent
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
557
            ms_parent_node = <_MergeSortNode>ms_node.left_parent.extra
4593.5.12 by John Arbash Meinel
Move some sets/dicts into attributes.
558
            if ms_node.is_first_child:
559
                # First child just increments the final digit
4593.5.34 by John Arbash Meinel
Change the KnownGraph.merge_sort api.
560
                ms_node._revno_first = ms_parent_node._revno_first
561
                ms_node._revno_second = ms_parent_node._revno_second
562
                ms_node._revno_last = ms_parent_node._revno_last + 1
4593.5.12 by John Arbash Meinel
Move some sets/dicts into attributes.
563
            else:
4593.5.10 by John Arbash Meinel
finish getting the revno numbers working correctly.
564
                # Not the first child, make a new branch
4593.5.27 by John Arbash Meinel
bit of cleanup passes
565
                #  (mainline_revno, branch_count, 1)
4593.5.34 by John Arbash Meinel
Change the KnownGraph.merge_sort api.
566
                if ms_parent_node._revno_first == -1:
4593.5.16 by John Arbash Meinel
Change the revno handling code.
567
                    # Mainline ancestor, the increment is on the last digit
4593.5.34 by John Arbash Meinel
Change the KnownGraph.merge_sort api.
568
                    base_revno = ms_parent_node._revno_last
4593.5.16 by John Arbash Meinel
Change the revno handling code.
569
                else:
4593.5.34 by John Arbash Meinel
Change the KnownGraph.merge_sort api.
570
                    base_revno = ms_parent_node._revno_first
4593.5.16 by John Arbash Meinel
Change the revno handling code.
571
                temp = PyDict_GetItem(self._revno_to_branch_count,
4593.5.17 by John Arbash Meinel
No need to pop one item off the stack after another.
572
                                      base_revno)
4593.5.16 by John Arbash Meinel
Change the revno handling code.
573
                if temp == NULL:
574
                    branch_count = 1
575
                else:
576
                    branch_count = (<object>temp) + 1
577
                PyDict_SetItem(self._revno_to_branch_count, base_revno,
578
                               branch_count)
4593.5.34 by John Arbash Meinel
Change the KnownGraph.merge_sort api.
579
                ms_node._revno_first = base_revno
580
                ms_node._revno_second = branch_count
581
                ms_node._revno_last = 1
4593.5.8 by John Arbash Meinel
Approximate implementation in Pyrex.
582
        else:
4593.5.27 by John Arbash Meinel
bit of cleanup passes
583
            temp = PyDict_GetItem(self._revno_to_branch_count, 0)
584
            if temp == NULL:
4593.5.16 by John Arbash Meinel
Change the revno handling code.
585
                # The first root node doesn't have a 3-digit revno
4593.5.27 by John Arbash Meinel
bit of cleanup passes
586
                root_count = 0
4593.5.34 by John Arbash Meinel
Change the KnownGraph.merge_sort api.
587
                ms_node._revno_first = -1
588
                ms_node._revno_second = -1
589
                ms_node._revno_last = 1
4593.5.27 by John Arbash Meinel
bit of cleanup passes
590
            else:
591
                root_count = (<object>temp) + 1
4593.5.34 by John Arbash Meinel
Change the KnownGraph.merge_sort api.
592
                ms_node._revno_first = 0
593
                ms_node._revno_second = root_count
594
                ms_node._revno_last = 1
4593.5.27 by John Arbash Meinel
bit of cleanup passes
595
            PyDict_SetItem(self._revno_to_branch_count, 0, root_count)
4593.5.12 by John Arbash Meinel
Move some sets/dicts into attributes.
596
        ms_node.completed = 1
4593.5.23 by John Arbash Meinel
Move the end-of-merge computation into _pop_node
597
        if PyList_GET_SIZE(self._scheduled_nodes) == 0:
4593.5.27 by John Arbash Meinel
bit of cleanup passes
598
            # The first scheduled node is always the end of merge
4593.5.23 by John Arbash Meinel
Move the end-of-merge computation into _pop_node
599
            ms_node.end_of_merge = True
600
        else:
601
            prev_node = _get_list_node(self._scheduled_nodes,
602
                                    PyList_GET_SIZE(self._scheduled_nodes) - 1)
603
            ms_prev_node = <_MergeSortNode>prev_node.extra
604
            if ms_prev_node.merge_depth < ms_node.merge_depth:
605
                # The previously pushed node is to our left, so this is the end
606
                # of this right-hand chain
607
                ms_node.end_of_merge = True
608
            elif (ms_prev_node.merge_depth == ms_node.merge_depth
609
                  and prev_node not in node.parents):
610
                # The next node is not a direct parent of this node
611
                ms_node.end_of_merge = True
612
            else:
613
                ms_node.end_of_merge = False
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
614
        PyList_Append(self._scheduled_nodes, node)
4593.5.8 by John Arbash Meinel
Approximate implementation in Pyrex.
615
616
    cdef _schedule_stack(self):
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
617
        cdef _KnownGraphNode last_node, next_node
4593.5.12 by John Arbash Meinel
Move some sets/dicts into attributes.
618
        cdef _MergeSortNode ms_node, ms_last_node, ms_next_node
4593.5.24 by John Arbash Meinel
Switch some variables from Py_ssize_t to long
619
        cdef long next_merge_depth
4593.5.8 by John Arbash Meinel
Approximate implementation in Pyrex.
620
        ordered = []
4593.5.15 by John Arbash Meinel
Use the same implementation of stack handling to avoid append/pop.
621
        while self._last_stack_item >= 0:
4593.5.8 by John Arbash Meinel
Approximate implementation in Pyrex.
622
            # Peek at the last item on the stack
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
623
            last_node = _get_list_node(self._depth_first_stack,
624
                                       self._last_stack_item)
4593.5.25 by John Arbash Meinel
Add tests that we detect GraphCycleError
625
            if last_node.gdfo == -1:
4593.5.28 by John Arbash Meinel
Cleanup pass
626
                # if _find_gdfo skipped a node, that means there is a graph
627
                # cycle, error out now
4593.5.25 by John Arbash Meinel
Add tests that we detect GraphCycleError
628
                raise errors.GraphCycleError(self.graph._nodes)
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
629
            ms_last_node = <_MergeSortNode>last_node.extra
4593.5.12 by John Arbash Meinel
Move some sets/dicts into attributes.
630
            if not ms_last_node.has_pending_parents():
631
                # Processed all parents, pop this node
4593.5.8 by John Arbash Meinel
Approximate implementation in Pyrex.
632
                self._pop_node()
633
                continue
4593.5.12 by John Arbash Meinel
Move some sets/dicts into attributes.
634
            while ms_last_node.has_pending_parents():
635
                if ms_last_node.left_pending_parent is not None:
4593.5.8 by John Arbash Meinel
Approximate implementation in Pyrex.
636
                    # recurse depth first into the primary parent
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
637
                    next_node = ms_last_node.left_pending_parent
4593.5.12 by John Arbash Meinel
Move some sets/dicts into attributes.
638
                    ms_last_node.left_pending_parent = None
4593.5.8 by John Arbash Meinel
Approximate implementation in Pyrex.
639
                else:
640
                    # place any merges in right-to-left order for scheduling
641
                    # which gives us left-to-right order after we reverse
4593.5.28 by John Arbash Meinel
Cleanup pass
642
                    # the scheduled queue.
643
                    # Note: This has the effect of allocating common-new
644
                    #       revisions to the right-most subtree rather than the
645
                    #       left most, which will display nicely (you get
646
                    #       smaller trees at the top of the combined merge).
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
647
                    next_node = ms_last_node.pending_parents.pop()
4593.5.27 by John Arbash Meinel
bit of cleanup passes
648
                ms_next_node = self._get_ms_node(next_node)
4593.5.12 by John Arbash Meinel
Move some sets/dicts into attributes.
649
                if ms_next_node.completed:
4593.5.8 by John Arbash Meinel
Approximate implementation in Pyrex.
650
                    # this parent was completed by a child on the
651
                    # call stack. skip it.
652
                    continue
653
                # otherwise transfer it from the source graph into the
654
                # top of the current depth first search stack.
655
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
656
                if next_node is ms_last_node.left_parent:
4593.5.24 by John Arbash Meinel
Switch some variables from Py_ssize_t to long
657
                    next_merge_depth = ms_last_node.merge_depth
4593.5.12 by John Arbash Meinel
Move some sets/dicts into attributes.
658
                else:
4593.5.24 by John Arbash Meinel
Switch some variables from Py_ssize_t to long
659
                    next_merge_depth = ms_last_node.merge_depth + 1
4593.5.19 by John Arbash Meinel
Switch from having a MergeSortNode => KnownGraphNode to
660
                self._push_node(next_node, next_merge_depth)
4593.5.8 by John Arbash Meinel
Approximate implementation in Pyrex.
661
                # and do not continue processing parents until this 'call'
662
                # has recursed.
663
                break
664
665
    cdef topo_order(self):
4593.5.23 by John Arbash Meinel
Move the end-of-merge computation into _pop_node
666
        cdef _MergeSortNode ms_node
667
        cdef _KnownGraphNode node
4593.5.17 by John Arbash Meinel
No need to pop one item off the stack after another.
668
        cdef Py_ssize_t pos
4593.5.28 by John Arbash Meinel
Cleanup pass
669
        cdef PyObject *temp_key, *temp_node
4593.5.8 by John Arbash Meinel
Approximate implementation in Pyrex.
670
4593.5.28 by John Arbash Meinel
Cleanup pass
671
        # Note: allocating a _MergeSortNode and deallocating it for all nodes
672
        #       costs approx 8.52ms (21%) of the total runtime
673
        #       We might consider moving the attributes into the base
674
        #       KnownGraph object.
4593.5.8 by John Arbash Meinel
Approximate implementation in Pyrex.
675
        self._schedule_stack()
676
677
        # We've set up the basic schedule, now we can continue processing the
678
        # output.
4593.5.29 by John Arbash Meinel
Remove the gc enable/disable code.
679
        # Note: This final loop costs us 40.0ms => 28.8ms (11ms, 25%) on
4593.5.24 by John Arbash Meinel
Switch some variables from Py_ssize_t to long
680
        #       bzr.dev, to convert the internal Object representation into a
681
        #       Tuple representation...
682
        #       2ms is walking the data and computing revno tuples
683
        #       7ms is computing the return tuple
684
        #       4ms is PyList_Append()
4593.5.8 by John Arbash Meinel
Approximate implementation in Pyrex.
685
        ordered = []
4593.5.34 by John Arbash Meinel
Change the KnownGraph.merge_sort api.
686
        # output the result in reverse order, and separate the generated info
4593.5.23 by John Arbash Meinel
Move the end-of-merge computation into _pop_node
687
        for pos from PyList_GET_SIZE(self._scheduled_nodes) > pos >= 0:
688
            node = _get_list_node(self._scheduled_nodes, pos)
689
            ms_node = <_MergeSortNode>node.extra
4593.5.34 by John Arbash Meinel
Change the KnownGraph.merge_sort api.
690
            PyList_Append(ordered, ms_node)
4593.5.23 by John Arbash Meinel
Move the end-of-merge computation into _pop_node
691
            node.extra = None
4593.5.34 by John Arbash Meinel
Change the KnownGraph.merge_sort api.
692
        # Clear out the scheduled nodes now that we're done
4593.5.17 by John Arbash Meinel
No need to pop one item off the stack after another.
693
        self._scheduled_nodes = []
4593.5.8 by John Arbash Meinel
Approximate implementation in Pyrex.
694
        return ordered