Read about our upcoming Code of Conduct on this issue

Commit 2c88404b authored by Armin Rigo's avatar Armin Rigo
Browse files

Tweak: 16% speed increase of PyPy annotation, by avoiding the

situation where a block is rescheduled many many times before it is
finally resolved (e.g. because resolving it requires (re)flowing
through a very long chain of blocks).
parent d00a16ef468f
......@@ -33,7 +33,7 @@ class RPythonAnnotator(object):
translator = TranslationContext()
translator.annotator = self
self.translator = translator
self.pendingblocks = {} # map {block: graph-containing-it}
self.genpendingblocks=[{}] # [{block: graph-containing-it}] * generation
self.annotated = {} # set of blocks already seen
self.added_blocks = None # see processblock() below
self.links_followed = {} # set of links that have ever been followed
......@@ -57,7 +57,7 @@ class RPythonAnnotator(object):
self.errors = []
def __getstate__(self):
attrs = """translator pendingblocks annotated links_followed
attrs = """translator genpendingblocks annotated links_followed
notify bookkeeper frozen policy added_blocks""".split()
ret = self.__dict__.copy()
for key, value in ret.items():
......@@ -188,18 +188,39 @@ class RPythonAnnotator(object):
else:
self.mergeinputargs(graph, block, cells)
if not self.annotated[block]:
self.pendingblocks[block] = graph
self.schedulependingblock(graph, block)
def schedulependingblock(self, graph, block):
# 'self.genpendingblocks' is a list of dictionaries which is
# logically equivalent to just one dictionary. But we keep a
# 'generation' number on each block (=key), and whenever we
# process a block, we increase its generation number. The
# block is added to the 'genpendingblocks' indexed by its
# generation number. See complete_pending_blocks() below.
generation = getattr(block, 'generation', 0)
self.genpendingblocks[generation][block] = graph
def complete_pending_blocks(self):
while self.pendingblocks:
# Grab all blocks from 'self.pendingblocks' in a list, and
# walk that list. This prevents a situation where the same
# block is added over and over again to 'self.pendingblocks'
# and the code here would pop that same block from the dict
# over and over again, without ever looking at other blocks.
all_blocks = self.pendingblocks.keys()
for block in all_blocks:
graph = self.pendingblocks.pop(block)
while True:
# Find the first of the dictionaries in 'self.genpendingblocks'
# which is not empty
gen = 0
for pendingblocks in self.genpendingblocks:
if pendingblocks:
break
gen += 1
else:
return # all empty => done
gen += 1 # next generation number
if len(self.genpendingblocks) == gen:
self.genpendingblocks.append({})
# Process all blocks at this level
# (if any gets re-inserted, it will be into the next level)
while pendingblocks:
block, graph = pendingblocks.popitem()
block.generation = gen
self.processblock(graph, block)
def complete(self):
......@@ -207,7 +228,7 @@ class RPythonAnnotator(object):
while True:
self.complete_pending_blocks()
self.policy.no_more_blocks_to_annotate(self)
if not self.pendingblocks:
if not any(self.genpendingblocks):
break # finished
# make sure that the return variables of all graphs is annotated
if self.added_blocks is not None:
......@@ -393,7 +414,7 @@ class RPythonAnnotator(object):
def reflowpendingblock(self, graph, block):
assert not self.frozen
assert graph not in self.fixed_graphs
self.pendingblocks[block] = graph
self.schedulependingblock(graph, block)
assert block in self.annotated
self.annotated[block] = False # must re-flow
self.blocked_blocks[block] = (graph, None)
......
......@@ -170,7 +170,7 @@ class Link(object):
class Block(object):
__slots__ = """inputargs operations exitswitch
exits blockcolor""".split()
exits blockcolor generation""".split()
def __init__(self, inputargs):
self.inputargs = list(inputargs) # mixed list of variable/const XXX
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment