comparison test/org/tmatesoft/hg/test/MapTagsToFileRevisions.java @ 326:d42a45a2c9d6

Alternative tag collection approach for a file history
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Tue, 04 Oct 2011 06:28:01 +0200
parents 283b294d1079
children 694ebabb5cb3
comparison
equal deleted inserted replaced
325:f05c8b1f08c4 326:d42a45a2c9d6
15 import org.tmatesoft.hg.core.HgChangeset; 15 import org.tmatesoft.hg.core.HgChangeset;
16 import org.tmatesoft.hg.core.HgChangesetHandler; 16 import org.tmatesoft.hg.core.HgChangesetHandler;
17 import org.tmatesoft.hg.core.HgException; 17 import org.tmatesoft.hg.core.HgException;
18 import org.tmatesoft.hg.core.HgLogCommand; 18 import org.tmatesoft.hg.core.HgLogCommand;
19 import org.tmatesoft.hg.core.Nodeid; 19 import org.tmatesoft.hg.core.Nodeid;
20 import org.tmatesoft.hg.internal.ArrayHelper; 20 import org.tmatesoft.hg.internal.IntMap;
21 import org.tmatesoft.hg.repo.HgChangelog; 21 import org.tmatesoft.hg.repo.HgChangelog;
22 import org.tmatesoft.hg.repo.HgChangelog.RawChangeset;
22 import org.tmatesoft.hg.repo.HgDataFile; 23 import org.tmatesoft.hg.repo.HgDataFile;
23 import org.tmatesoft.hg.repo.HgLookup; 24 import org.tmatesoft.hg.repo.HgLookup;
24 import org.tmatesoft.hg.repo.HgManifest; 25 import org.tmatesoft.hg.repo.HgManifest;
26 import org.tmatesoft.hg.repo.HgManifest.Flags;
25 import org.tmatesoft.hg.repo.HgRepository; 27 import org.tmatesoft.hg.repo.HgRepository;
26 import org.tmatesoft.hg.repo.HgTags; 28 import org.tmatesoft.hg.repo.HgTags;
27 import org.tmatesoft.hg.repo.HgChangelog.RawChangeset;
28 import org.tmatesoft.hg.repo.HgManifest.Flags;
29 import org.tmatesoft.hg.repo.HgTags.TagInfo; 29 import org.tmatesoft.hg.repo.HgTags.TagInfo;
30 import org.tmatesoft.hg.util.CancelledException; 30 import org.tmatesoft.hg.util.CancelledException;
31 import org.tmatesoft.hg.util.Path; 31 import org.tmatesoft.hg.util.Path;
32 32
33 /** 33 /**
38 // Static ================================================================= 38 // Static =================================================================
39 39
40 public static void main(String[] args) throws Exception { 40 public static void main(String[] args) throws Exception {
41 MapTagsToFileRevisions m = new MapTagsToFileRevisions(); 41 MapTagsToFileRevisions m = new MapTagsToFileRevisions();
42 System.out.printf("Free mem: %,d\n", Runtime.getRuntime().freeMemory()); 42 System.out.printf("Free mem: %,d\n", Runtime.getRuntime().freeMemory());
43 // m.collectTagsPerFile(); 43 m.collectTagsPerFile();
44 // m.manifestWalk(); 44 // m.manifestWalk();
45 // m.changelogWalk(); 45 // m.changelogWalk();
46 // m.revisionMap(); 46 // m.revisionMap();
47 m.buildFile2ChangelogRevisionMap(); 47 // m.buildFile2ChangelogRevisionMap();
48 m = null; 48 m = null;
49 System.gc(); 49 System.gc();
50 System.out.printf("Free mem: %,d\n", Runtime.getRuntime().freeMemory()); 50 System.out.printf("Free mem: %,d\n", Runtime.getRuntime().freeMemory());
51 } 51 }
52 52
198 // 0..10k is 34 seconds now 198 // 0..10k is 34 seconds now
199 // Another run, 23 seconds now, seems nothing has been changed. Switched to Pool2 with DirectHashSet: 22,5 seconds 199 // Another run, 23 seconds now, seems nothing has been changed. Switched to Pool2 with DirectHashSet: 22,5 seconds
200 System.out.printf("Total time: %d ms\n", System.currentTimeMillis() - start); 200 System.out.printf("Total time: %d ms\n", System.currentTimeMillis() - start);
201 System.out.printf("Free mem: %,d\n", Runtime.getRuntime().freeMemory()); 201 System.out.printf("Free mem: %,d\n", Runtime.getRuntime().freeMemory());
202 } 202 }
203 203
204 private void collectTagsPerFile() throws HgException, CancelledException { 204 private int[] collectLocalTagRevisions(HgChangelog.RevisionMap clogrmap, TagInfo[] allTags, IntMap<List<TagInfo>> tagLocalRev2TagInfo) {
205 final long start = System.currentTimeMillis();
206 final HgRepository repository = new HgLookup().detect(new File("/temp/hg/cpython"));
207 final HgTags tags = repository.getTags();
208 //
209 // build cache
210 //
211 final TagInfo[] allTags = new TagInfo[tags.getTags().size()];
212 tags.getTags().values().toArray(allTags);
213 // file2rev2tag value is array of revisions, always of allTags.length. Revision index in the array
214 // is index of corresponding TagInfo in allTags;
215 final Map<Path, Nodeid[]> file2rev2tag = new HashMap<Path, Nodeid[]>();
216 System.out.printf("Collecting manifests for %d tags\n", allTags.length);
217 // effective translation of changeset revisions to their local indexes
218 final HgChangelog.RevisionMap clogrmap = repository.getChangelog().new RevisionMap().init();
219 int[] tagLocalRevs = new int[allTags.length]; 205 int[] tagLocalRevs = new int[allTags.length];
220 int x = 0; 206 int x = 0;
221 for (int i = 0; i < allTags.length; i++) { 207 for (int i = 0; i < allTags.length; i++) {
222 final Nodeid tagRevision = allTags[i].revision(); 208 final Nodeid tagRevision = allTags[i].revision();
223 final int tagLocalRev = clogrmap.localRevision(tagRevision); 209 final int tagLocalRev = clogrmap.localRevision(tagRevision);
224 if (tagLocalRev != HgRepository.BAD_REVISION) { 210 if (tagLocalRev != HgRepository.BAD_REVISION) {
225 tagLocalRevs[x++] = tagLocalRev; 211 tagLocalRevs[x++] = tagLocalRev;
212 List<TagInfo> tagsAssociatedWithRevision = tagLocalRev2TagInfo.get(tagLocalRev);
213 if (tagsAssociatedWithRevision == null) {
214 tagLocalRev2TagInfo.put(tagLocalRev, tagsAssociatedWithRevision = new LinkedList<TagInfo>());
215 }
216 tagsAssociatedWithRevision.add(allTags[i]);
226 } 217 }
227 } 218 }
228 if (x != allTags.length) { 219 if (x != allTags.length) {
229 // some tags were removed (recorded Nodeid.NULL tagname) 220 // some tags were removed (recorded Nodeid.NULL tagname)
230 int[] copy = new int[x]; 221 int[] copy = new int[x];
231 System.arraycopy(tagLocalRevs, 0, copy, 0, x); 222 System.arraycopy(tagLocalRevs, 0, copy, 0, x);
232 tagLocalRevs = copy; 223 tagLocalRevs = copy;
233 } 224 }
225 return tagLocalRevs;
226 }
227
228 private void collectTagsPerFile() throws HgException, CancelledException {
229 final long start = System.currentTimeMillis();
230 final HgRepository repository = new HgLookup().detect(new File("/temp/hg/cpython"));
231 final HgTags tags = repository.getTags();
232 //
233 // build cache
234 //
235 final TagInfo[] allTags = new TagInfo[tags.getTags().size()];
236 tags.getTags().values().toArray(allTags);
237 // effective translation of changeset revisions to their local indexes
238 final HgChangelog.RevisionMap clogrmap = repository.getChangelog().new RevisionMap().init();
239 // map to look up tag by changeset local number
240 final IntMap<List<TagInfo>> tagLocalRev2TagInfo = new IntMap<List<TagInfo>>(allTags.length);
241 System.out.printf("Collecting manifests for %d tags\n", allTags.length);
242 final int[] tagLocalRevs = collectLocalTagRevisions(clogrmap, allTags, tagLocalRev2TagInfo);
234 System.out.printf("Prepared tag revisions to analyze: %d ms\n", System.currentTimeMillis() - start); 243 System.out.printf("Prepared tag revisions to analyze: %d ms\n", System.currentTimeMillis() - start);
235 // 244
245 final Path targetPath = Path.create("README");
246 //
247 collectTagsPerFile_Approach_1(clogrmap, tagLocalRevs, allTags, targetPath);
248 System.out.printf("Total time: %d ms\n", System.currentTimeMillis() - start);
249
250 System.out.println("\nApproach 2");
251 collectTagsPerFile_Approach_2(repository, tagLocalRevs, tagLocalRev2TagInfo, allTags, targetPath);
252 }
253
254 // Approach 1. Build map with all files, their revisions and corresponding tags
255 //
256 private void collectTagsPerFile_Approach_1(final HgChangelog.RevisionMap clogrmap, final int[] tagLocalRevs, final TagInfo[] allTags, Path targetPath) {
257 HgRepository repository = clogrmap.getRepo();
258 final long start = System.currentTimeMillis();
259 // file2rev2tag value is array of revisions, always of allTags.length. Revision index in the array
260 // is index of corresponding TagInfo in allTags;
261 final Map<Path, Nodeid[]> file2rev2tag = new HashMap<Path, Nodeid[]>();
236 repository.getManifest().walk(new HgManifest.Inspector2() { 262 repository.getManifest().walk(new HgManifest.Inspector2() {
237 private int[] tagIndexAtRev = new int[4]; // it's unlikely there would be a lot of tags associated with a given cset 263 private int[] tagIndexAtRev = new int[4]; // it's unlikely there would be a lot of tags associated with a given cset
238 264
239 public boolean begin(int mainfestRevision, Nodeid nid, int changelogRevision) { 265 public boolean begin(int mainfestRevision, Nodeid nid, int changelogRevision) {
266 // may do better here using tagLocalRev2TagInfo, but need to change a lot, too lazy now
240 Nodeid cset = clogrmap.revision(changelogRevision); 267 Nodeid cset = clogrmap.revision(changelogRevision);
241 Arrays.fill(tagIndexAtRev, -1); 268 Arrays.fill(tagIndexAtRev, -1);
242 for (int i = 0, x = 0; i < allTags.length; i++) { 269 for (int i = 0, x = 0; i < allTags.length; i++) {
243 if (cset.equals(allTags[i].revision())) { 270 if (cset.equals(allTags[i].revision())) {
244 tagIndexAtRev[x++] = i; 271 tagIndexAtRev[x++] = i;
284 311
285 }, tagLocalRevs); 312 }, tagLocalRevs);
286 System.out.printf("Cache built: %d ms\n", System.currentTimeMillis() - start); 313 System.out.printf("Cache built: %d ms\n", System.currentTimeMillis() - start);
287 // 314 //
288 // look up specific file. This part is fast. 315 // look up specific file. This part is fast.
289 final Path targetPath = Path.create("README");
290 HgDataFile fileNode = repository.getFileNode(targetPath); 316 HgDataFile fileNode = repository.getFileNode(targetPath);
291 final Nodeid[] allTagsOfTheFile = file2rev2tag.get(targetPath); 317 final Nodeid[] allTagsOfTheFile = file2rev2tag.get(targetPath);
292 // TODO if fileNode.isCopy, repeat for each getCopySourceName() 318 // TODO if fileNode.isCopy, repeat for each getCopySourceName()
293 for (int localFileRev = 0; localFileRev < fileNode.getRevisionCount(); localFileRev++) { 319 for (int localFileRev = 0; localFileRev < fileNode.getRevisionCount(); localFileRev++) {
294 Nodeid fileRev = fileNode.getRevision(localFileRev); 320 Nodeid fileRev = fileNode.getRevision(localFileRev);
299 associatedTags.add(allTags[i].name()); 325 associatedTags.add(allTags[i].name());
300 } 326 }
301 } 327 }
302 System.out.printf("%3d%7d%s\n", localFileRev, changesetLocalRev, associatedTags); 328 System.out.printf("%3d%7d%s\n", localFileRev, changesetLocalRev, associatedTags);
303 } 329 }
304 System.out.printf("Total time: %d ms\n", System.currentTimeMillis() - start); 330 }
331
332 private void collectTagsPerFile_Approach_2(HgRepository repository, final int[] tagLocalRevs, final IntMap<List<TagInfo>> tagLocalRev2TagInfo, TagInfo[] allTags, Path targetPath) {
333 //
334 // Approach 2. No all-file map. Collect file revisions recorded at the time of tagging,
335 // then for each file revision check if it is among those above, and if yes, take corresponding tags
336 HgDataFile fileNode = repository.getFileNode(targetPath);
337 final long start2 = System.nanoTime();
338 final int lastRev = fileNode.getLastRevision();
339 final Map<Integer, Nodeid> fileRevisionAtTagRevision = repository.getManifest().getFileRevisions(targetPath, tagLocalRevs);
340 final long start2a = System.nanoTime();
341 fileNode.walk(0, lastRev, new HgDataFile.RevisionInspector() {
342
343 public void next(int localFileRev, Nodeid fileRevision, int linkedRevision) {
344 int changesetLocalRev = linkedRevision;
345 List<String> associatedTags = new LinkedList<String>();
346 for (int taggetRevision : tagLocalRevs) {
347 // current file revision can't appear in tags that point to earlier changelog revisions (they got own file revision)
348 if (taggetRevision >= changesetLocalRev) {
349 // z points to some changeset with tag
350 Nodeid wasKnownAs = fileRevisionAtTagRevision.get(taggetRevision);
351 if (wasKnownAs.equals(fileRevision)) {
352 // has tag associated with changeset at index z
353 List<TagInfo> tagsAtRev = tagLocalRev2TagInfo.get(taggetRevision);
354 assert tagsAtRev != null;
355 for (TagInfo ti : tagsAtRev) {
356 associatedTags.add(ti.name());
357 }
358 }
359 }
360 }
361 System.out.printf("%3d%7d%s\n", localFileRev, changesetLocalRev, associatedTags);
362 }
363 });
364 System.out.printf("Alternative total time: %d ms, of that init: %d ms\n", (System.nanoTime() - start2)/1000000, (start2a-start2)/1000000);
305 System.out.printf("Free mem: %,d\n", Runtime.getRuntime().freeMemory()); 365 System.out.printf("Free mem: %,d\n", Runtime.getRuntime().freeMemory());
306 } 366 }
307 367
308 public static void main2(String[] args) throws HgException, CancelledException { 368 public static void main2(String[] args) throws HgException, CancelledException {
309 final HgRepository repository = new HgLookup().detect(new File("/temp/hg/cpython")); 369 final HgRepository repository = new HgLookup().detect(new File("/temp/hg/cpython"));