# HG changeset patch # User Durham Goode # Date 1509219354 25200 # Node ID ffeea2406276980990b897473ce85de531c2d614 # Parent 6e66033f91ccfb7666ec0a25d19954673269c61a dirstate: remove excess attribute lookups for dirstate.status (issue5714) A recent refactor added a layer of abstraction to the dirstate which makes doing things like 'foo in dirstate' now require some extra Python attribute lookups. This is causing a 100ms slow down in hg status for mozilla-central. The fix is to hoist the inner dict's functions onto the main class once the lazy loading it complete, as well as store the actual functions before doing the status loop (as is done for other such functions). In my testing, it seems to address the performance regression, but we'll need to see the perf run results to know for sure. Differential Revision: https://phab.mercurial-scm.org/D1257 diff -r 6e66033f91cc -r ffeea2406276 mercurial/dirstate.py --- a/mercurial/dirstate.py Thu Oct 26 16:15:36 2017 -0700 +++ b/mercurial/dirstate.py Sat Oct 28 12:35:54 2017 -0700 @@ -1053,6 +1053,9 @@ removed, deleted, clean = [], [], [] dmap = self._map + dmap.preload() + dcontains = dmap.__contains__ + dget = dmap.__getitem__ ladd = lookup.append # aka "unsure" madd = modified.append aadd = added.append @@ -1074,7 +1077,7 @@ full = listclean or match.traversedir is not None for fn, st in self.walk(match, subrepos, listunknown, listignored, full=full).iteritems(): - if fn not in dmap: + if not dcontains(fn): if (listignored or mexact(fn)) and dirignore(fn): if listignored: iadd(fn) @@ -1089,7 +1092,7 @@ # a list, but falls back to creating a full-fledged iterator in # general. That is much slower than simply accessing and storing the # tuple members one by one. - t = dmap[fn] + t = dget(fn) state = t[0] mode = t[1] size = t[2] @@ -1216,8 +1219,8 @@ return self.copymap def clear(self): - self._map = {} - self.copymap = {} + self._map.clear() + self.copymap.clear() self.setparents(nullid, nullid) def iteritems(self): @@ -1247,6 +1250,10 @@ def keys(self): return self._map.keys() + def preload(self): + """Loads the underlying data, if it's not already loaded""" + self._map + def nonnormalentries(self): '''Compute the nonnormal dirstate entries from the dmap''' try: @@ -1373,6 +1380,13 @@ if not self._dirtyparents: self.setparents(*p) + # Avoid excess attribute lookups by fast pathing certain checks + self.__contains__ = self._map.__contains__ + self.__getitem__ = self._map.__getitem__ + self.__setitem__ = self._map.__setitem__ + self.__delitem__ = self._map.__delitem__ + self.get = self._map.get + def write(self, st, now): st.write(parsers.pack_dirstate(self._map, self.copymap, self.parents(), now))