# HG changeset patch # User Jun Wu # Date 1506199572 25200 # Node ID bd50aa1aa035429e7cd099aae8f3b2db388e561b # Parent 0e48813cc106ca63600d77d2847a6721a7288ae3 alias: make alias command lazily resolved With many aliases, resolving them could have some visible overhead. Below is part of traceprof [1] output of `hg bookmark --hidden`: (time unit: ms) 37 \ addaliases dispatch.py:526 37 | __init__ (60 times) dispatch.py:402 33 | findcmd (108 times) cmdutil.py:721 16 | findpossible (49 times) cmdutil.py:683 It may get better by optimizing `findcmd` to do a bisect, but we don't really need to resolve an alias if it's not used, so let's make those command entries lazy. After this patch, `addalias` takes less than 1ms. .. perf:: improved performance when many aliases are defined [1]: https://bitbucket.org/facebook/hg-experimental/src/9aca0dbdbdfc48457e5d2581ca2d6e662fced2e6/hgext3rd/traceprof.pyx Differential Revision: https://phab.mercurial-scm.org/D805 diff -r 0e48813cc106 -r bd50aa1aa035 mercurial/dispatch.py --- a/mercurial/dispatch.py Sat Sep 23 13:31:09 2017 -0700 +++ b/mercurial/dispatch.py Sat Sep 23 13:46:12 2017 -0700 @@ -523,21 +523,52 @@ ui.debug("alias '%s' expands to '%s'\n" % (self.name, args)) raise +class lazyaliasentry(object): + """like a typical command entry (func, opts, help), but is lazy""" + + def __init__(self, name, definition, cmdtable, source): + self.name = name + self.definition = definition + self.cmdtable = cmdtable.copy() + self.source = source + + @util.propertycache + def _aliasdef(self): + return cmdalias(self.name, self.definition, self.cmdtable, self.source) + + def __getitem__(self, n): + aliasdef = self._aliasdef + if n == 0: + return aliasdef + elif n == 1: + return aliasdef.opts + elif n == 2: + return aliasdef.help + else: + raise IndexError + + def __iter__(self): + for i in range(3): + yield self[i] + + def __len__(self): + return 3 + def addaliases(ui, cmdtable): # aliases are processed after extensions have been loaded, so they # may use extension commands. Aliases can also use other alias definitions, # but only if they have been defined prior to the current definition. for alias, definition in ui.configitems('alias'): try: - if cmdtable[alias][0].definition == definition: + if cmdtable[alias].definition == definition: continue except (KeyError, AttributeError): # definition might not exist or it might not be a cmdalias pass source = ui.configsource('alias', alias) - aliasdef = cmdalias(alias, definition, cmdtable, source) - cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help) + entry = lazyaliasentry(alias, definition, cmdtable, source) + cmdtable[alias] = entry def _parse(ui, args): options = {}