Skip to content

Commit e378504

Browse files
Merge pull request #400 from benjeffery/node-traversal-perf
Perf improvments to node traversals
2 parents 628db5d + fccc952 commit e378504

File tree

1 file changed

+32
-14
lines changed

1 file changed

+32
-14
lines changed

python/tskit/trees.py

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1411,23 +1411,36 @@ def num_tracked_samples(self, u=None):
14111411
return sum(self._ll_tree.get_num_tracked_samples(root) for root in roots)
14121412

14131413
def _preorder_traversal(self, u):
1414-
stack = [u]
1415-
while len(stack) > 0:
1416-
v = stack.pop()
1417-
if self.is_internal(v):
1418-
stack.extend(reversed(self.children(v)))
1414+
stack = collections.deque([u])
1415+
# For perf we store these to avoid lookups in the tight loop
1416+
pop = stack.pop
1417+
extend = stack.extend
1418+
get_children = self.children
1419+
# Note: the usual style is to be explicit about what we're testing
1420+
# and use while len(stack) > 0, but this form is slightly faster.
1421+
while stack:
1422+
v = pop()
1423+
extend(reversed(get_children(v)))
14191424
yield v
14201425

14211426
def _postorder_traversal(self, u):
1422-
stack = [u]
1427+
stack = collections.deque([u])
14231428
parent = NULL
1424-
while len(stack) > 0:
1429+
# For perf we store these to avoid lookups in the tight loop
1430+
pop = stack.pop
1431+
extend = stack.extend
1432+
get_children = self.children
1433+
get_parent = self.get_parent
1434+
# Note: the usual style is to be explicit about what we're testing
1435+
# and use while len(stack) > 0, but this form is slightly faster.
1436+
while stack:
14251437
v = stack[-1]
1426-
if self.is_internal(v) and v != parent:
1427-
stack.extend(reversed(self.children(v)))
1438+
children = [] if v == parent else get_children(v)
1439+
if children:
1440+
extend(reversed(children))
14281441
else:
1429-
parent = self.parent(v)
1430-
yield stack.pop()
1442+
parent = get_parent(v)
1443+
yield pop()
14311444

14321445
def _inorder_traversal(self, u):
14331446
# TODO add a nonrecursive version of the inorder traversal.
@@ -1443,10 +1456,15 @@ def _inorder_traversal(self, u):
14431456

14441457
def _levelorder_traversal(self, u):
14451458
queue = collections.deque([u])
1459+
# For perf we store these to avoid lookups in the tight loop
1460+
pop = queue.popleft
1461+
extend = queue.extend
1462+
children = self.children
1463+
# Note: the usual style is to be explicit about what we're testing
1464+
# and use while len(queue) > 0, but this form is slightly faster.
14461465
while queue:
1447-
v = queue.popleft()
1448-
if self.is_internal(v):
1449-
queue.extend(self.children(v))
1466+
v = pop()
1467+
extend(children(v))
14501468
yield v
14511469

14521470
def _timeasc_traversal(self, u):

0 commit comments

Comments
 (0)