/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
4371.3.21 by John Arbash Meinel
Attempt to make things better by splitting out left_parent and right_parent into
47
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
48
from bzrlib import revision
49
4371.4.21 by John Arbash Meinel
Spend some time optimizing the inner heads() loop, approx 2x faster.
50
cdef object NULL_REVISION
51
NULL_REVISION = revision.NULL_REVISION
52
53
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
54
cdef class _KnownGraphNode:
55
    """Represents a single object in the known graph."""
56
4371.3.35 by John Arbash Meinel
Changing the heapq algorithm actually ends up with better overall performance.
57
    cdef object key
4371.3.28 by John Arbash Meinel
Finish converting away from 'cdef list' syntax.
58
    cdef object parents
59
    cdef object children
4371.4.13 by Vincent Ladeuil
Fixed as per John's review.
60
    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.
61
    cdef int seen
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
62
4371.3.31 by John Arbash Meinel
Change _KnownGraph.parents to be a tuple rather than a list.
63
    def __init__(self, key):
4371.3.21 by John Arbash Meinel
Attempt to make things better by splitting out left_parent and right_parent into
64
        cdef int i
65
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
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
73
74
    property child_keys:
75
        def __get__(self):
76
            cdef _KnownGraphNode child
77
78
            keys = []
79
            for child in self.children:
4371.3.28 by John Arbash Meinel
Finish converting away from 'cdef list' syntax.
80
                PyList_Append(keys, child.key)
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
81
            return keys
82
83
    cdef clear_references(self):
84
        self.parents = None
85
        self.children = None
86
87
    def __repr__(self):
4371.3.35 by John Arbash Meinel
Changing the heapq algorithm actually ends up with better overall performance.
88
        cdef _KnownGraphNode node
89
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
90
        parent_keys = []
4371.3.35 by John Arbash Meinel
Changing the heapq algorithm actually ends up with better overall performance.
91
        if self.parents is not None:
92
            for node in self.parents:
93
                parent_keys.append(node.key)
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
94
        child_keys = []
4371.3.35 by John Arbash Meinel
Changing the heapq algorithm actually ends up with better overall performance.
95
        if self.children is not None:
96
            for node in self.children:
97
                child_keys.append(node.key)
4371.4.10 by Vincent Ladeuil
Cleanup.
98
        return '%s(%s  gdfo:%s par:%s child:%s)' % (
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
99
            self.__class__.__name__, self.key, self.gdfo,
4371.4.12 by Vincent Ladeuil
Fix typos.
100
            parent_keys, child_keys)
4371.4.10 by Vincent Ladeuil
Cleanup.
101
4371.3.37 by John Arbash Meinel
cleanup passes.
102
4371.4.14 by John Arbash Meinel
Optimize _find_gdfo and make known_parent_gdfos a member of KnownGraphNode
103
cdef _KnownGraphNode _get_list_node(lst, Py_ssize_t pos):
104
    cdef PyObject *temp_node
105
106
    temp_node = PyList_GET_ITEM(lst, pos)
107
    return <_KnownGraphNode>temp_node
108
109
110
cdef _KnownGraphNode _get_parent(parents, Py_ssize_t pos):
111
    cdef PyObject *temp_node
112
    cdef _KnownGraphNode node
113
114
    temp_node = PyTuple_GET_ITEM(parents, pos)
115
    return <_KnownGraphNode>temp_node
116
117
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
118
# TODO: slab allocate all _KnownGraphNode objects.
4371.3.26 by John Arbash Meinel
A few more cleanups. Start moving away from pyrex 0.9.8isms
119
#       We already know how many we are going to need, except for a couple of
120
#       ghosts that could be allocated on demand.
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
121
122
cdef class KnownGraph:
123
    """This is a class which assumes we already know the full graph."""
124
125
    cdef public object _nodes
4371.3.26 by John Arbash Meinel
A few more cleanups. Start moving away from pyrex 0.9.8isms
126
    cdef object _known_heads
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
127
    cdef public int do_cache
128
129
    def __init__(self, parent_map, do_cache=True):
130
        """Create a new KnownGraph instance.
131
132
        :param parent_map: A dictionary mapping key => parent_keys
133
        """
4371.4.29 by John Arbash Meinel
Pre-allocating self._nodes turned out to be slower.
134
        # tests at pre-allocating the node dict actually slowed things down
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
135
        self._nodes = {}
136
        # Maps {sorted(revision_id, revision_id): heads}
137
        self._known_heads = {}
138
        self.do_cache = int(do_cache)
139
        self._initialize_nodes(parent_map)
140
        self._find_gdfo()
141
142
    def __dealloc__(self):
143
        cdef _KnownGraphNode child
4371.3.28 by John Arbash Meinel
Finish converting away from 'cdef list' syntax.
144
        cdef Py_ssize_t pos
145
        cdef PyObject *temp_node
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
146
4371.3.28 by John Arbash Meinel
Finish converting away from 'cdef list' syntax.
147
        while PyDict_Next(self._nodes, &pos, NULL, &temp_node):
148
            child = <_KnownGraphNode>temp_node
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
149
            child.clear_references()
150
4371.4.28 by John Arbash Meinel
We spent a lot of time adding and removing keys from tails.
151
    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.
152
        cdef PyObject *temp_node
153
        cdef _KnownGraphNode node
154
155
        temp_node = PyDict_GetItem(self._nodes, key)
156
        if temp_node == NULL:
157
            node = _KnownGraphNode(key)
158
            PyDict_SetItem(self._nodes, key, node)
159
        else:
160
            node = <_KnownGraphNode>temp_node
161
        return node
162
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
163
    def _initialize_nodes(self, parent_map):
164
        """Populate self._nodes.
165
4371.4.9 by Vincent Ladeuil
Simplify gdfo computing by finding tails when at graph build time.
166
        After this has finished:
167
        - self._nodes will have an entry for every entry in parent_map.
168
        - ghosts will have a parent_keys = None,
4371.4.12 by Vincent Ladeuil
Fix typos.
169
        - all nodes found will also have child_keys populated with all known
170
          child keys,
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
171
        """
4371.3.28 by John Arbash Meinel
Finish converting away from 'cdef list' syntax.
172
        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.
173
        cdef Py_ssize_t pos, pos2, num_parent_keys
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
174
        cdef _KnownGraphNode node
175
        cdef _KnownGraphNode parent_node
176
4371.3.28 by John Arbash Meinel
Finish converting away from 'cdef list' syntax.
177
        if not PyDict_CheckExact(parent_map):
178
            raise TypeError('parent_map should be a dict of {key:parent_keys}')
179
        # for key, parent_keys in parent_map.iteritems():
180
        pos = 0
181
        while PyDict_Next(parent_map, &pos, &temp_key, &temp_parent_keys):
182
            key = <object>temp_key
183
            parent_keys = <object>temp_parent_keys
4371.4.9 by Vincent Ladeuil
Simplify gdfo computing by finding tails when at graph build time.
184
            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.
185
            node = self._get_or_create_node(key)
4371.3.31 by John Arbash Meinel
Change _KnownGraph.parents to be a tuple rather than a list.
186
            # We know how many parents, so we could pre allocate an exact sized
187
            # tuple here
188
            parent_nodes = PyTuple_New(num_parent_keys)
189
            # We use iter here, because parent_keys maybe be a list or tuple
190
            for pos2 from 0 <= pos2 < num_parent_keys:
4371.4.28 by John Arbash Meinel
We spent a lot of time adding and removing keys from tails.
191
                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.
192
                # PyTuple_SET_ITEM will steal a reference, so INCREF first
193
                Py_INCREF(parent_node)
194
                PyTuple_SET_ITEM(parent_nodes, pos2, parent_node)
4371.3.28 by John Arbash Meinel
Finish converting away from 'cdef list' syntax.
195
                PyList_Append(parent_node.children, node)
196
            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.
197
198
    def _find_tails(self):
199
        cdef PyObject *temp_node
200
        cdef _KnownGraphNode node
201
        cdef Py_ssize_t pos
202
203
        tails = []
4371.4.28 by John Arbash Meinel
We spent a lot of time adding and removing keys from tails.
204
        pos = 0
205
        while PyDict_Next(self._nodes, &pos, NULL, &temp_node):
206
            node = <_KnownGraphNode>temp_node
207
            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.
208
                node.gdfo = 1
209
                PyList_Append(tails, node)
210
        return tails
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
211
212
    def _find_gdfo(self):
4371.4.8 by Vincent Ladeuil
Replace the existing KnownGraph._find_gdfo.
213
        cdef _KnownGraphNode node
214
        cdef _KnownGraphNode child
4371.4.14 by John Arbash Meinel
Optimize _find_gdfo and make known_parent_gdfos a member of KnownGraphNode
215
        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.
216
        cdef Py_ssize_t pos
4371.4.14 by John Arbash Meinel
Optimize _find_gdfo and make known_parent_gdfos a member of KnownGraphNode
217
        cdef int replace
4371.4.26 by John Arbash Meinel
Don't shrink the pending list.
218
        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.
219
        cdef long next_gdfo
4371.4.8 by Vincent Ladeuil
Replace the existing KnownGraph._find_gdfo.
220
4371.4.30 by John Arbash Meinel
We don't need self._tails anymore, nor does it need to be a set.
221
        pending = self._find_tails()
4371.4.8 by Vincent Ladeuil
Replace the existing KnownGraph._find_gdfo.
222
4371.4.26 by John Arbash Meinel
Don't shrink the pending list.
223
        last_item = PyList_GET_SIZE(pending) - 1
224
        while last_item >= 0:
4371.4.14 by John Arbash Meinel
Optimize _find_gdfo and make known_parent_gdfos a member of KnownGraphNode
225
            # 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.
226
            # timing shows this is 930ms => 770ms for OOo
4371.4.26 by John Arbash Meinel
Don't shrink the pending list.
227
            node = _get_list_node(pending, last_item)
228
            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.
229
            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.
230
            for pos from 0 <= pos < PyList_GET_SIZE(node.children):
231
                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.
232
                if next_gdfo > child.gdfo:
233
                    child.gdfo = next_gdfo
4371.4.27 by John Arbash Meinel
Re-use the child.seen attribute for tracking num parents.
234
                child.seen = child.seen + 1
235
                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.
236
                    # This child is populated, queue it to be walked
4371.4.26 by John Arbash Meinel
Don't shrink the pending list.
237
                    last_item = last_item + 1
238
                    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
239
                        Py_INCREF(child) # SetItem steals a ref
4371.4.26 by John Arbash Meinel
Don't shrink the pending list.
240
                        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
241
                    else:
242
                        PyList_Append(pending, child)
4371.4.27 by John Arbash Meinel
Re-use the child.seen attribute for tracking num parents.
243
                    # We have queued this node, we don't need to track it
244
                    # anymore
245
                    child.seen = 0
4371.4.8 by Vincent Ladeuil
Replace the existing KnownGraph._find_gdfo.
246
4371.3.19 by John Arbash Meinel
Get an initial pyrex implementation.
247
    def heads(self, keys):
248
        """Return the heads from amongst keys.
249
250
        This is done by searching the ancestries of each key.  Any key that is
251
        reachable from another key is not returned; all the others are.
252
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
253
        This operation scales with the relative depth between any two keys. It
254
        uses gdfo to avoid walking all ancestry.
255
256
        :param keys: An iterable of keys.
257
        :return: A set of the heads. Note that as a set there is no ordering
258
            information. Callers will need to filter their input to create
259
            order if they need it.
260
        """
261
        cdef PyObject *maybe_node
262
        cdef PyObject *maybe_heads
263
        cdef PyObject *temp_node
264
        cdef _KnownGraphNode node
4371.4.31 by John Arbash Meinel
Apply the same 'never pop' logic to the heads() code.
265
        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.
266
        cdef long min_gdfo
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
267
4526.10.1 by John Arbash Meinel
Avoid using PyFrozenSet_New
268
        heads_key = frozenset(keys)
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
269
        maybe_heads = PyDict_GetItem(self._known_heads, heads_key)
270
        if maybe_heads != NULL:
271
            return <object>maybe_heads
272
        # Not cached, compute it ourselves
273
        candidate_nodes = {}
274
        for key in keys:
4371.4.28 by John Arbash Meinel
We spent a lot of time adding and removing keys from tails.
275
            maybe_node = PyDict_GetItem(self._nodes, key)
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
276
            if maybe_node == NULL:
277
                raise KeyError('key %s not in nodes' % (key,))
278
            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.
279
        maybe_node = PyDict_GetItem(candidate_nodes, NULL_REVISION)
280
        if maybe_node != NULL:
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
281
            # 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.
282
            candidate_nodes.pop(NULL_REVISION)
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
283
            if not candidate_nodes:
4371.4.21 by John Arbash Meinel
Spend some time optimizing the inner heads() loop, approx 2x faster.
284
                return frozenset([NULL_REVISION])
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
285
            # The keys changed, so recalculate heads_key
4526.10.1 by John Arbash Meinel
Avoid using PyFrozenSet_New
286
            heads_key = frozenset(candidate_nodes)
4371.4.21 by John Arbash Meinel
Spend some time optimizing the inner heads() loop, approx 2x faster.
287
        if PyDict_Size(candidate_nodes) < 2:
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
288
            return heads_key
289
4371.4.22 by John Arbash Meinel
Changing from a set of seen to a list to cleanup and a seen attribute.
290
        cleanup = []
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
291
        pending = []
4371.4.21 by John Arbash Meinel
Spend some time optimizing the inner heads() loop, approx 2x faster.
292
        # we know a gdfo cannot be longer than a linear chain of all nodes
293
        min_gdfo = PyDict_Size(self._nodes) + 1
294
        # Build up nodes that need to be walked, note that starting nodes are
295
        # not added to seen()
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
296
        pos = 0
297
        while PyDict_Next(candidate_nodes, &pos, NULL, &temp_node):
298
            node = <_KnownGraphNode>temp_node
299
            if node.parents is not None:
300
                pending.extend(node.parents)
4371.4.21 by John Arbash Meinel
Spend some time optimizing the inner heads() loop, approx 2x faster.
301
            if node.gdfo < min_gdfo:
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
302
                min_gdfo = node.gdfo
4371.4.21 by John Arbash Meinel
Spend some time optimizing the inner heads() loop, approx 2x faster.
303
304
        # Now do all the real work
4371.4.31 by John Arbash Meinel
Apply the same 'never pop' logic to the heads() code.
305
        last_item = PyList_GET_SIZE(pending) - 1
306
        while last_item >= 0:
307
            node = _get_list_node(pending, last_item)
308
            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.
309
            if node.seen:
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
310
                # node already appears in some ancestry
311
                continue
4371.4.22 by John Arbash Meinel
Changing from a set of seen to a list to cleanup and a seen attribute.
312
            PyList_Append(cleanup, node)
313
            node.seen = 1
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
314
            if node.gdfo <= min_gdfo:
315
                continue
4371.4.21 by John Arbash Meinel
Spend some time optimizing the inner heads() loop, approx 2x faster.
316
            if node.parents is not None and PyTuple_GET_SIZE(node.parents) > 0:
317
                for pos from 0 <= pos < PyTuple_GET_SIZE(node.parents):
318
                    parent_node = _get_parent(node.parents, pos)
4371.4.31 by John Arbash Meinel
Apply the same 'never pop' logic to the heads() code.
319
                    last_item = last_item + 1
320
                    if last_item < PyList_GET_SIZE(pending):
321
                        Py_INCREF(parent_node) # SetItem steals a ref
322
                        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.
323
                    else:
324
                        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.
325
        heads = []
326
        pos = 0
327
        while PyDict_Next(candidate_nodes, &pos, NULL, &temp_node):
328
            node = <_KnownGraphNode>temp_node
329
            if not node.seen:
330
                PyList_Append(heads, node.key)
4526.10.1 by John Arbash Meinel
Avoid using PyFrozenSet_New
331
        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.
332
        for pos from 0 <= pos < PyList_GET_SIZE(cleanup):
333
            node = _get_list_node(cleanup, pos)
334
            node.seen = 0
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
335
        if self.do_cache:
4371.4.21 by John Arbash Meinel
Spend some time optimizing the inner heads() loop, approx 2x faster.
336
            PyDict_SetItem(self._known_heads, heads_key, heads)
4371.4.7 by Vincent Ladeuil
Simple Pyrex version for the new heads() method.
337
        return heads