Mercurial > hg4j
diff src/org/tmatesoft/hg/internal/RevlogStream.java @ 593:9619301a7bb9
Share last revision read between #iterate() invocations, to save revision rebuild efforts when few subsequent revisions are read
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Thu, 02 May 2013 16:51:02 +0200 |
parents | b47ef0d2777b |
children | cc7b0c4dc993 |
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/internal/RevlogStream.java Wed May 01 14:03:10 2013 +0200 +++ b/src/org/tmatesoft/hg/internal/RevlogStream.java Thu May 02 16:51:02 2013 +0200 @@ -22,6 +22,9 @@ import java.io.File; import java.io.IOException; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; import java.util.zip.Inflater; import org.tmatesoft.hg.core.Nodeid; @@ -59,6 +62,14 @@ private final File indexFile; private File dataFile; private final DataAccessProvider dataAccess; + // keeps last complete revision we've read. Note, this cached revision doesn't help + // for subsequent #iterate() calls with the same revision (Inspector needs more data than + // we currently cache here, perhaps, we shall cache everything it wants to cover same + // revision case as well). Now this helps when second #iterate() call is for a revision greater + // than one from the first call, and both revisions got same base rev. It's often the case when + // parents/children are analyzed. + private SoftReference<CachedRevision> lastRevisionRead; + private final ReferenceQueue<CachedRevision> lastRevisionQueue = new ReferenceQueue<CachedRevision>(); // if we need anything else from HgRepo, might replace DAP parameter with HgRepo and query it for DAP. public RevlogStream(DataAccessProvider dap, File indexFile) { @@ -260,8 +271,6 @@ } } - - // should be possible to use TIP, ALL, or -1, -2, -n notation of Hg // ? boolean needsNodeid public void iterate(int start, int end, boolean needData, Inspector inspector) throws HgInvalidRevisionException, HgInvalidControlFileException { @@ -281,14 +290,15 @@ ReaderN1 r = new ReaderN1(needData, inspector, dataAccess.shallMergePatches()); try { - r.start(end - start + 1); + r.start(end - start + 1, getLastRevisionRead()); r.range(start, end); } catch (IOException ex) { throw new HgInvalidControlFileException(String.format("Failed reading [%d..%d]", start, end), ex, indexFile); } catch (HgInvalidControlFileException ex) { throw ex; } finally { - r.finish(); + CachedRevision cr = r.finish(); + setLastRevisionRead(cr); } } @@ -313,7 +323,7 @@ ReaderN1 r = new ReaderN1(needData, inspector, dataAccess.shallMergePatches()); try { - r.start(sortedRevisions.length); + r.start(sortedRevisions.length, lastRevisionRead == null ? null : lastRevisionRead.get()); for (int i = 0; i < sortedRevisions.length; ) { int x = i; i++; @@ -336,7 +346,8 @@ // TODO post-1.0 fill HgRuntimeException with appropriate file (either index or data, depending on error source) throw ex; } finally { - r.finish(); + CachedRevision cr = r.finish(); + setLastRevisionRead(cr); } } @@ -471,6 +482,37 @@ } } + private CachedRevision getLastRevisionRead() { + return lastRevisionRead == null ? null : lastRevisionRead.get(); + } + + private void setLastRevisionRead(CachedRevision cr) { + // done() for lastRevisionRead.userData has been called by ReaderN1 once + // it noticed unsuitable DataAccess. + // Now, done() for any CachedRevision cleared by GC: + for (Reference<? extends CachedRevision> r; (r = lastRevisionQueue.poll()) != null;) { + CachedRevision toClean = r.get(); + if (toClean != null && toClean.userData != null) { + toClean.userData.done(); + } + } + if (cr != null) { + lastRevisionRead = new SoftReference<CachedRevision>(cr, lastRevisionQueue); + } else { + lastRevisionRead = null; + } + } + + final static class CachedRevision { + final int revision; + final DataAccess userData; + + public CachedRevision(int lastRevisionRead, DataAccess lastUserData) { + revision = lastRevisionRead; + userData = lastUserData; + } + } + /** * operation with single file open/close and multiple diverse reads. * XXX initOutline might need similar extraction to keep N1 format knowledge @@ -500,8 +542,6 @@ private int linkRevision; private int parent1Revision; private int parent2Revision; - // next are to track two major bottlenecks - patch application and actual time spent in inspector -// private long applyTime, inspectorTime; // TIMING public ReaderN1(boolean dataRequested, Inspector insp, boolean usePatchMerge) { assert insp != null; @@ -510,7 +550,7 @@ mergePatches = usePatchMerge; } - public void start(int totalWork) { + public void start(int totalWork, CachedRevision cachedRevision) { daIndex = getIndexStream(); if (needData && !inline) { daData = getDataStream(); @@ -520,13 +560,18 @@ cb = new Lifecycle.BasicCallback(); lifecycleListener.start(totalWork, cb, cb); } -// applyTime = inspectorTime = 0; // TIMING + if (needData && cachedRevision != null) { + lastUserData = cachedRevision.userData; + lastRevisionRead = cachedRevision.revision; + assert lastUserData != null; + } } // invoked only once per instance - public void finish() { + public CachedRevision finish() { + CachedRevision rv = null; if (lastUserData != null) { - lastUserData.done(); + rv = new CachedRevision(lastRevisionRead, lastUserData); lastUserData = null; } if (lifecycleListener != null) { @@ -540,7 +585,7 @@ daData.done(); daData = null; } -// System.out.printf("applyTime:%d ms, inspectorTime: %d ms\n", applyTime, inspectorTime); // TIMING + return rv; } private void readHeaderRecord(int i) throws IOException {