Mercurial > hg
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 |