comparison mercurial/config.py @ 34357: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
comparison
equal deleted inserted replaced
34350:f975cb7c4dbe 34357:c41444a39de2
18 ) 18 )
19 19
20 class config(object): 20 class config(object):
21 def __init__(self, data=None, includepaths=None): 21 def __init__(self, data=None, includepaths=None):
22 self._data = {} 22 self._data = {}
23 self._source = {}
24 self._unset = [] 23 self._unset = []
25 self._includepaths = includepaths or [] 24 self._includepaths = includepaths or []
26 if data: 25 if data:
27 for k in data._data: 26 for k in data._data:
28 self._data[k] = data[k].copy() 27 self._data[k] = data[k].copy()
29 self._source = data._source.copy() 28 self._source = data._source.copy()
29 else:
30 self._source = util.cowdict()
30 def copy(self): 31 def copy(self):
31 return config(self) 32 return config(self)
32 def __contains__(self, section): 33 def __contains__(self, section):
33 return section in self._data 34 return section in self._data
34 def hasitem(self, section, item): 35 def hasitem(self, section, item):
37 return self._data.get(section, {}) 38 return self._data.get(section, {})
38 def __iter__(self): 39 def __iter__(self):
39 for d in self.sections(): 40 for d in self.sections():
40 yield d 41 yield d
41 def update(self, src): 42 def update(self, src):
43 self._source = self._source.preparewrite()
42 for s, n in src._unset: 44 for s, n in src._unset:
43 if s in self and n in self._data[s]: 45 ds = self._data.get(s, None)
46 if ds is not None and n in ds:
47 self._data[s] = ds.preparewrite()
44 del self._data[s][n] 48 del self._data[s][n]
45 del self._source[(s, n)] 49 del self._source[(s, n)]
46 for s in src: 50 for s in src:
47 if s not in self: 51 ds = self._data.get(s, None)
48 self._data[s] = util.sortdict() 52 if ds:
53 self._data[s] = ds.preparewrite()
54 else:
55 self._data[s] = util.cowsortdict()
49 self._data[s].update(src._data[s]) 56 self._data[s].update(src._data[s])
50 self._source.update(src._source) 57 self._source.update(src._source)
51 def get(self, section, item, default=None): 58 def get(self, section, item, default=None):
52 return self._data.get(section, {}).get(item, default) 59 return self._data.get(section, {}).get(item, default)
53 60
72 def set(self, section, item, value, source=""): 79 def set(self, section, item, value, source=""):
73 if pycompat.ispy3: 80 if pycompat.ispy3:
74 assert not isinstance(value, str), ( 81 assert not isinstance(value, str), (
75 'config values may not be unicode strings on Python 3') 82 'config values may not be unicode strings on Python 3')
76 if section not in self: 83 if section not in self:
77 self._data[section] = util.sortdict() 84 self._data[section] = util.cowsortdict()
85 else:
86 self._data[section] = self._data[section].preparewrite()
78 self._data[section][item] = value 87 self._data[section][item] = value
79 if source: 88 if source:
89 self._source = self._source.preparewrite()
80 self._source[(section, item)] = source 90 self._source[(section, item)] = source
81 91
82 def restore(self, data): 92 def restore(self, data):
83 """restore data returned by self.backup""" 93 """restore data returned by self.backup"""
94 self._source = self._source.preparewrite()
84 if len(data) == 4: 95 if len(data) == 4:
85 # restore old data 96 # restore old data
86 section, item, value, source = data 97 section, item, value, source = data
98 self._data[section] = self._data[section].preparewrite()
87 self._data[section][item] = value 99 self._data[section][item] = value
88 self._source[(section, item)] = source 100 self._source[(section, item)] = source
89 else: 101 else:
90 # no data before, remove everything 102 # no data before, remove everything
91 section, item = data 103 section, item = data
147 if m: 159 if m:
148 section = m.group(1) 160 section = m.group(1)
149 if remap: 161 if remap:
150 section = remap.get(section, section) 162 section = remap.get(section, section)
151 if section not in self: 163 if section not in self:
152 self._data[section] = util.sortdict() 164 self._data[section] = util.cowsortdict()
153 continue 165 continue
154 m = itemre.match(l) 166 m = itemre.match(l)
155 if m: 167 if m:
156 item = m.group(1) 168 item = m.group(1)
157 cont = True 169 cont = True