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
--- 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 = {}