changeset 255:5a6ab50b4cbf

Improve memory footprint of tag collection (about 14 Mb saved for cpython repo without HashMap.Entry and Entry[])
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Tue, 16 Aug 2011 14:33:11 +0200
parents a620f0663a37
children b61ed0f2c4da
files test/org/tmatesoft/hg/test/MapTagsToFileRevisions.java
diffstat 1 files changed, 65 insertions(+), 32 deletions(-) [+]
line wrap: on
line diff
--- a/test/org/tmatesoft/hg/test/MapTagsToFileRevisions.java	Tue Aug 16 04:03:29 2011 +0200
+++ b/test/org/tmatesoft/hg/test/MapTagsToFileRevisions.java	Tue Aug 16 14:33:11 2011 +0200
@@ -1,14 +1,27 @@
 package org.tmatesoft.hg.test;
 
-import java.io.*;
-import java.util.*;
-import java.util.Map.Entry;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
 
-import org.tmatesoft.hg.core.*;
+import org.tmatesoft.hg.core.HgChangeset;
+import org.tmatesoft.hg.core.HgChangesetHandler;
+import org.tmatesoft.hg.core.HgException;
+import org.tmatesoft.hg.core.HgLogCommand;
+import org.tmatesoft.hg.core.Nodeid;
 import org.tmatesoft.hg.internal.Pool;
-import org.tmatesoft.hg.repo.*;
+import org.tmatesoft.hg.repo.HgChangelog;
+import org.tmatesoft.hg.repo.HgDataFile;
+import org.tmatesoft.hg.repo.HgLookup;
+import org.tmatesoft.hg.repo.HgManifest;
+import org.tmatesoft.hg.repo.HgRepository;
+import org.tmatesoft.hg.repo.HgTags;
 import org.tmatesoft.hg.repo.HgTags.TagInfo;
-import org.tmatesoft.hg.util.*;
+import org.tmatesoft.hg.util.CancelledException;
+import org.tmatesoft.hg.util.Path;
 
 /**
  * @author Marc Strapetz
@@ -17,34 +30,51 @@
 
 	// Static =================================================================
 
-	public static void main(String[] args) throws HgException, CancelledException {
+	public static void main(String[] args) throws Exception {
+		MapTagsToFileRevisions m = new MapTagsToFileRevisions();
+		System.out.printf("Free mem: %,d\n", Runtime.getRuntime().freeMemory());
+		m.main();
+		m = null;
+		System.gc();
+		System.out.printf("Free mem: %,d\n", Runtime.getRuntime().freeMemory());
+	}
+	
+	private void main() throws HgException, CancelledException {
 		final long start = System.currentTimeMillis();
 		final HgRepository repository = new HgLookup().detect(new File("/temp/hg/cpython"));
 		final HgTags tags = repository.getTags();
 		//
 		// build cache
-		final Map<String, Map<TagInfo, Nodeid>> file2tag2rev = new HashMap<String, Map<TagInfo, Nodeid>>();
-		System.out.printf("Collecting manifests for %d tags\n", tags.getTags().size());
+		//
+		final TagInfo[] allTags = new TagInfo[tags.getTags().size()];
+		tags.getTags().values().toArray(allTags);
+		// file2rev2tag value is array of revisions, always of allTags.length. Revision index in the array
+		// is index of corresponding TagInfo in allTags;
+		final Map<String, Nodeid[]> file2rev2tag = new HashMap<String, Nodeid[]>();
+		System.out.printf("Collecting manifests for %d tags\n", allTags.length);
 		// effective translation of changeset revisions to their local indexes
 		final HgChangelog.RevisionMap clogrmap = repository.getChangelog().new RevisionMap().init();
-		int[] tagLocalRevs = new int[tags.getTags().size()];
-		int i = 0;
-		for (TagInfo tag : tags.getTags().values()) {
-			final Nodeid tagRevision = tag.revision();
-			int tagLocalRev = clogrmap.localRevision(tagRevision);
-			tagLocalRevs[i++] = tagLocalRev;
+		int[] tagLocalRevs = new int[allTags.length];
+		for (int i = 0; i < allTags.length; i++) {
+			final Nodeid tagRevision = allTags[i].revision();
+			tagLocalRevs[i] = clogrmap.localRevision(tagRevision);
 		}
-		System.out.printf("Found tag revisions to analyze: %d\n", System.currentTimeMillis() - start);
+		System.out.printf("Prepared tag revisions to analyze: %d ms\n", System.currentTimeMillis() - start);
 		//
 		repository.getManifest().walk(new HgManifest.Inspector() {
-			private List<String> tagsAtRev;
-			final Pool<String> filenamePool = new Pool<String>();
-			final Pool<Nodeid> nodeidPool = new Pool<Nodeid>();
+			private final ArrayList<Integer> tagIndexAtRev = new ArrayList<Integer>();
+			private final Pool<String> filenamePool = new Pool<String>();
+			private final Pool<Nodeid> nodeidPool = new Pool<Nodeid>();
 
 			public boolean begin(int mainfestRevision, Nodeid nid, int changelogRevision) {
 				Nodeid cset = clogrmap.revision(changelogRevision);
-				tagsAtRev = tags.tags(cset);
-				if (tagsAtRev.isEmpty()) {
+				tagIndexAtRev.clear();
+				for (int i = 0; i < allTags.length; i++) {
+					if (cset.equals(allTags[i].revision())) {
+						tagIndexAtRev.add(i);
+					}
+				}
+				if (tagIndexAtRev.isEmpty()) {
 					System.out.println("Can't happen, provided we iterate over revisions with tags only");
 				}
 				return true;
@@ -53,12 +83,15 @@
 			public boolean next(Nodeid nid, String fname, String flags) {
 				fname = filenamePool.unify(fname);
 				nid = nodeidPool.unify(nid);
-				Map<TagInfo, Nodeid> m = file2tag2rev.get(fname);
+				Nodeid[] m = file2rev2tag.get(fname);
 				if (m == null) {
-					file2tag2rev.put(fname, m = new HashMap<TagInfo, Nodeid>());
+					file2rev2tag.put(fname, m = new Nodeid[allTags.length]);
 				}
-				for (String tag : tagsAtRev) {
-					m.put(tags.getTags().get(tag), nid);
+				for (int tagIndex : tagIndexAtRev) {
+					if (m[tagIndex] != null) {
+						System.out.printf("There's another revision (%s) associated with tag %s already while we try to associate %s\n", m[tagIndex].shortNotation(), allTags[tagIndex].name(), nid.shortNotation());
+					}
+					m[tagIndex] = nid;
 				}
 				return true;
 			}
@@ -68,26 +101,26 @@
 			}
 			
 		}, tagLocalRevs);
-		System.out.printf("Cache built: %d\n", System.currentTimeMillis() - start);
+		System.out.printf("Cache built: %d ms\n", System.currentTimeMillis() - start);
 		//
 		// look up specific file. This part is fast.
 		final Path targetPath = Path.create("README");
 		HgDataFile fileNode = repository.getFileNode(targetPath);
+		final Nodeid[] allTagsOfTheFile = file2rev2tag.get(targetPath.toString());
 		// TODO if fileNode.isCopy, repeat for each getCopySourceName()
 		for (int localFileRev = 0; localFileRev < fileNode.getRevisionCount(); localFileRev++) {
 			Nodeid fileRev = fileNode.getRevision(localFileRev);
 			int changesetLocalRev = fileNode.getChangesetLocalRevision(localFileRev);
 			List<String> associatedTags = new LinkedList<String>();
-			final Map<TagInfo, Nodeid> allTagsOfTheFile = file2tag2rev.get(targetPath.toString());
-			for (Entry<TagInfo, Nodeid> e : allTagsOfTheFile.entrySet()) {
-				Nodeid fileRevAtTag = e.getValue();
-				if (fileRev.equals(fileRevAtTag)) {
-					associatedTags.add(e.getKey().name());
+			for (int i = 0; i < allTagsOfTheFile.length; i++) {
+				if (fileRev.equals(allTagsOfTheFile[i])) {
+					associatedTags.add(allTags[i].name());
 				}
 			}
 			System.out.printf("%3d%7d%s\n", localFileRev, changesetLocalRev, associatedTags);
 		}
-		System.out.printf("Total time: %d", System.currentTimeMillis() - start);
+		System.out.printf("Total time: %d ms\n", System.currentTimeMillis() - start);
+		System.out.printf("Free mem: %,d\n", Runtime.getRuntime().freeMemory());
 	}
 
 	public static void main2(String[] args) throws HgException, CancelledException {