Mercurial > hg
comparison mercurial/repoview.py @ 18445:4d92e2d75cff
repoview: cache filtered changelog
Creating a new changelog object for each access is costly and prevents efficient
caching changelog side. This introduced a x5 performance regression in log
because chunk read from disk were never reused. We were jumping from about 100
disk read to about 20 000.
This changeset introduce a simple cache mechanism that help the last changelog
object created by a repoview. The changelog is reused until the changelog or the
filtering changes.
The cache invalidation is much more complicated than it should be. But MQ test
show a strange cache desync. I was unable to track down the source of this
desync in descent time so I'm not sure if the issue is in MQ or core. However
given the proximity to the 2.5 freeze, I'm choosing the inelegant but safe route
that makes the cache mechanism safer.
author | Pierre-Yves David <pierre-yves.david@logilab.fr> |
---|---|
date | Fri, 18 Jan 2013 23:43:32 +0100 |
parents | 64848f7fb764 |
children | 593eb3786165 |
comparison
equal
deleted
inserted
replaced
18444:55aff0c2b73c | 18445:4d92e2d75cff |
---|---|
153 """ | 153 """ |
154 | 154 |
155 def __init__(self, repo, filtername): | 155 def __init__(self, repo, filtername): |
156 object.__setattr__(self, '_unfilteredrepo', repo) | 156 object.__setattr__(self, '_unfilteredrepo', repo) |
157 object.__setattr__(self, 'filtername', filtername) | 157 object.__setattr__(self, 'filtername', filtername) |
158 object.__setattr__(self, '_clcachekey', None) | |
159 object.__setattr__(self, '_clcache', None) | |
158 | 160 |
159 # not a cacheproperty on purpose we shall implement a proper cache later | 161 # not a cacheproperty on purpose we shall implement a proper cache later |
160 @property | 162 @property |
161 def changelog(self): | 163 def changelog(self): |
162 """return a filtered version of the changeset | 164 """return a filtered version of the changeset |
163 | 165 |
164 this changelog must not be used for writing""" | 166 this changelog must not be used for writing""" |
165 # some cache may be implemented later | 167 # some cache may be implemented later |
166 cl = copy.copy(self._unfilteredrepo.changelog) | 168 unfi = self._unfilteredrepo |
167 cl.filteredrevs = filterrevs(self._unfilteredrepo, self.filtername) | 169 unfichangelog = unfi.changelog |
170 revs = filterrevs(unfi, self.filtername) | |
171 cl = self._clcache | |
172 newkey = (len(unfichangelog), unfichangelog.tip(), hash(revs)) | |
173 if cl is not None: | |
174 # we need to check curkey too for some obscure reason. | |
175 # MQ test show a corruption of the underlying repo (in _clcache) | |
176 # without change in the cachekey. | |
177 oldfilter = cl.filteredrevs | |
178 try: | |
179 cl.filterrevs = () # disable filtering for tip | |
180 curkey = (len(cl), cl.tip(), hash(oldfilter)) | |
181 finally: | |
182 cl.filteredrevs = oldfilter | |
183 if newkey != self._clcachekey or newkey != curkey: | |
184 cl = None | |
185 # could have been made None by the previous if | |
186 if cl is None: | |
187 cl = copy.copy(unfichangelog) | |
188 cl.filteredrevs = revs | |
189 object.__setattr__(self, '_clcache', cl) | |
190 object.__setattr__(self, '_clcachekey', newkey) | |
168 return cl | 191 return cl |
169 | 192 |
170 def unfiltered(self): | 193 def unfiltered(self): |
171 """Return an unfiltered version of a repo""" | 194 """Return an unfiltered version of a repo""" |
172 return self._unfilteredrepo | 195 return self._unfilteredrepo |