Mercurial > public > mercurial-scm > hg-stable
diff mercurial/config.py @ 34358:c41444a39de2
config: use copy-on-write to improve copy performance
Previously, chg's `verify` call could take 30+ms loading and checking new
config files. With one socket redirection, that adds up to around 70ms,
which is a lot for fast commands (ex. `bookmark --hidden`).
When investigating closer, A lot of time was spent on actually spent on ui
copying, which is mainly about `config.config` and `dict` copying.
This patch makes that 20x faster by adopting copy-on-write. The
copy-on-write is performed at config section level.
Before:
In [1]: %timeit ui.copy()
100 loops, best of 3: 2.32 ms per loop
After:
In [1]: %timeit ui.copy()
10000 loops, best of 3: 128 us per loop
2ms may look not that bad, but it adds up pretty quickly with multiple
calls. A typical chg run may call it 4 times, which is about 10ms.
Differential Revision: https://phab.mercurial-scm.org/D808
author | Jun Wu <quark@fb.com> |
---|---|
date | Wed, 27 Sep 2017 18:07:48 -0700 |
parents | 0fa781320203 |
children | 0efdfb57b05c |
line wrap: on
line diff
--- a/mercurial/config.py Sat Sep 30 18:19:14 2017 +0530 +++ b/mercurial/config.py Wed Sep 27 18:07:48 2017 -0700 @@ -20,13 +20,14 @@ class config(object): def __init__(self, data=None, includepaths=None): self._data = {} - self._source = {} self._unset = [] self._includepaths = includepaths or [] if data: for k in data._data: self._data[k] = data[k].copy() self._source = data._source.copy() + else: + self._source = util.cowdict() def copy(self): return config(self) def __contains__(self, section): @@ -39,13 +40,19 @@ for d in self.sections(): yield d def update(self, src): + self._source = self._source.preparewrite() for s, n in src._unset: - if s in self and n in self._data[s]: + ds = self._data.get(s, None) + if ds is not None and n in ds: + self._data[s] = ds.preparewrite() del self._data[s][n] del self._source[(s, n)] for s in src: - if s not in self: - self._data[s] = util.sortdict() + ds = self._data.get(s, None) + if ds: + self._data[s] = ds.preparewrite() + else: + self._data[s] = util.cowsortdict() self._data[s].update(src._data[s]) self._source.update(src._source) def get(self, section, item, default=None): @@ -74,16 +81,21 @@ assert not isinstance(value, str), ( 'config values may not be unicode strings on Python 3') if section not in self: - self._data[section] = util.sortdict() + self._data[section] = util.cowsortdict() + else: + self._data[section] = self._data[section].preparewrite() self._data[section][item] = value if source: + self._source = self._source.preparewrite() self._source[(section, item)] = source def restore(self, data): """restore data returned by self.backup""" + self._source = self._source.preparewrite() if len(data) == 4: # restore old data section, item, value, source = data + self._data[section] = self._data[section].preparewrite() self._data[section][item] = value self._source[(section, item)] = source else: @@ -149,7 +161,7 @@ if remap: section = remap.get(section, section) if section not in self: - self._data[section] = util.sortdict() + self._data[section] = util.cowsortdict() continue m = itemre.match(l) if m: