comparison mercurial/manifest.py @ 18604:a1141f04e368

manifest: use a size 3 LRU cache to store parsed manifests Previously, the manifest cache would store the last manifest parsed. We could run into situations with operations like update where we would try parsing the manifest for a revision r1, then r2, then r1 again. This increases the cache size to 3 to avoid that bit of performance fragility.
author Siddharth Agarwal <sid0@fb.com>
date Sat, 09 Feb 2013 15:43:02 +0000
parents c64e646af81e
children 40b4b1f9b7a0
comparison
equal deleted inserted replaced
18603:2251b3184e6e 18604:a1141f04e368
26 def copy(self): 26 def copy(self):
27 return manifestdict(self, dict.copy(self._flags)) 27 return manifestdict(self, dict.copy(self._flags))
28 28
29 class manifest(revlog.revlog): 29 class manifest(revlog.revlog):
30 def __init__(self, opener): 30 def __init__(self, opener):
31 self._mancache = None 31 # we expect to deal with not more than three revs at a time in merge
32 self._mancache = util.lrucachedict(3)
32 revlog.revlog.__init__(self, opener, "00manifest.i") 33 revlog.revlog.__init__(self, opener, "00manifest.i")
33 34
34 def parse(self, lines): 35 def parse(self, lines):
35 mfdict = manifestdict() 36 mfdict = manifestdict()
36 parsers.parse_manifest(mfdict, mfdict._flags, lines) 37 parsers.parse_manifest(mfdict, mfdict._flags, lines)
49 return self.read(node) 50 return self.read(node)
50 51
51 def read(self, node): 52 def read(self, node):
52 if node == revlog.nullid: 53 if node == revlog.nullid:
53 return manifestdict() # don't upset local cache 54 return manifestdict() # don't upset local cache
54 if self._mancache and self._mancache[0] == node: 55 if node in self._mancache:
55 return self._mancache[1] 56 return self._mancache[node][0]
56 text = self.revision(node) 57 text = self.revision(node)
57 arraytext = array.array('c', text) 58 arraytext = array.array('c', text)
58 mapping = self.parse(text) 59 mapping = self.parse(text)
59 self._mancache = (node, mapping, arraytext) 60 self._mancache[node] = (mapping, arraytext)
60 return mapping 61 return mapping
61 62
62 def _search(self, m, s, lo=0, hi=None): 63 def _search(self, m, s, lo=0, hi=None):
63 '''return a tuple (start, end) that says where to find s within m. 64 '''return a tuple (start, end) that says where to find s within m.
64 65
100 return (lo, lo) 101 return (lo, lo)
101 102
102 def find(self, node, f): 103 def find(self, node, f):
103 '''look up entry for a single file efficiently. 104 '''look up entry for a single file efficiently.
104 return (node, flags) pair if found, (None, None) if not.''' 105 return (node, flags) pair if found, (None, None) if not.'''
105 if self._mancache and self._mancache[0] == node: 106 if node in self._mancache:
106 return self._mancache[1].get(f), self._mancache[1].flags(f) 107 mapping = self._mancache[node][0]
108 return mapping.get(f), mapping.flags(f)
107 text = self.revision(node) 109 text = self.revision(node)
108 start, end = self._search(text, f) 110 start, end = self._search(text, f)
109 if start == end: 111 if start == end:
110 return None, None 112 return None, None
111 l = text[start:end] 113 l = text[start:end]
141 raise error.RevlogError( 143 raise error.RevlogError(
142 _("'\\n' and '\\r' disallowed in filenames: %r") % f) 144 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
143 145
144 # if we're using the cache, make sure it is valid and 146 # if we're using the cache, make sure it is valid and
145 # parented by the same node we're diffing against 147 # parented by the same node we're diffing against
146 if not (changed and self._mancache and p1 and self._mancache[0] == p1): 148 if not (changed and p1 and (p1 in self._mancache)):
147 files = sorted(map) 149 files = sorted(map)
148 checkforbidden(files) 150 checkforbidden(files)
149 151
150 # if this is changed to support newlines in filenames, 152 # if this is changed to support newlines in filenames,
151 # be sure to check the templates/ dir again (especially *-raw.tmpl) 153 # be sure to check the templates/ dir again (especially *-raw.tmpl)
154 for f in files) 156 for f in files)
155 arraytext = array.array('c', text) 157 arraytext = array.array('c', text)
156 cachedelta = None 158 cachedelta = None
157 else: 159 else:
158 added, removed = changed 160 added, removed = changed
159 addlist = self._mancache[2] 161 addlist = self._mancache[p1][1]
160 162
161 checkforbidden(added) 163 checkforbidden(added)
162 # combine the changed lists into one list for sorting 164 # combine the changed lists into one list for sorting
163 work = [(x, False) for x in added] 165 work = [(x, False) for x in added]
164 work.extend((x, True) for x in removed) 166 work.extend((x, True) for x in removed)
206 cachedelta = (self.rev(p1), deltatext) 208 cachedelta = (self.rev(p1), deltatext)
207 arraytext = addlist 209 arraytext = addlist
208 text = util.buffer(arraytext) 210 text = util.buffer(arraytext)
209 211
210 n = self.addrevision(text, transaction, link, p1, p2, cachedelta) 212 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
211 self._mancache = (n, map, arraytext) 213 self._mancache[n] = (map, arraytext)
212 214
213 return n 215 return n