comparison src/org/tmatesoft/hg/repo/HgParentChildMap.java @ 656:a937e63b6e02

Performance: rebuild information about branches takes too long (my improvement: 3 times, 11-15 s to less than 4 sec)
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Thu, 04 Jul 2013 18:40:03 +0200
parents 629a7370554c
children 6334b0267103
comparison
equal deleted inserted replaced
655:bcbcc318f250 656:a937e63b6e02
26 import java.util.HashSet; 26 import java.util.HashSet;
27 import java.util.LinkedList; 27 import java.util.LinkedList;
28 import java.util.List; 28 import java.util.List;
29 29
30 import org.tmatesoft.hg.core.Nodeid; 30 import org.tmatesoft.hg.core.Nodeid;
31 import org.tmatesoft.hg.internal.IntMap;
31 import org.tmatesoft.hg.repo.Revlog.ParentInspector; 32 import org.tmatesoft.hg.repo.Revlog.ParentInspector;
32 33
33 /** 34 /**
34 * Helper class to deal with parent-child relationship between revisions <i>en masse</i>. 35 * Helper class to deal with parent-child relationship between revisions <i>en masse</i>.
35 * Works in terms of {@link Nodeid nodeids}, there's no need to deal with revision indexes. 36 * Works in terms of {@link Nodeid nodeids}, there's no need to deal with revision indexes.
65 private Nodeid[] sorted; // for binary search 66 private Nodeid[] sorted; // for binary search
66 private int[] sorted2natural; // indexes in sorted to indexes in sequential 67 private int[] sorted2natural; // indexes in sorted to indexes in sequential
67 private Nodeid[] firstParent; // parents by natural order (i.e. firstParent[A] is parent of revision with index A) 68 private Nodeid[] firstParent; // parents by natural order (i.e. firstParent[A] is parent of revision with index A)
68 private Nodeid[] secondParent; 69 private Nodeid[] secondParent;
69 private final T revlog; 70 private final T revlog;
70 private BitSet heads; // 1 indicates revision got children 71 private IntMap<Nodeid> heads;
72 private BitSet headsBitSet; // 1 indicates revision got children, != null only during init;
71 73
72 74
73 public HgParentChildMap(T owner) { 75 public HgParentChildMap(T owner) {
74 revlog = owner; 76 revlog = owner;
75 } 77 }
84 } 86 }
85 int ix = revisionNumber; 87 int ix = revisionNumber;
86 sequential[ix] = sorted[ix] = revision; 88 sequential[ix] = sorted[ix] = revision;
87 if (parent1Revision != -1) { 89 if (parent1Revision != -1) {
88 firstParent[ix] = sequential[parent1Revision]; 90 firstParent[ix] = sequential[parent1Revision];
89 heads.set(parent1Revision); 91 headsBitSet.set(parent1Revision);
90 } 92 }
91 if (parent2Revision != -1) { // revlog of DataAccess.java has p2 set when p1 is -1 93 if (parent2Revision != -1) { // revlog of DataAccess.java has p2 set when p1 is -1
92 secondParent[ix] = sequential[parent2Revision]; 94 secondParent[ix] = sequential[parent2Revision];
93 heads.set(parent2Revision); 95 headsBitSet.set(parent2Revision);
94 } 96 }
95 } 97 }
96 98
97 /** 99 /**
98 * Prepare the map 100 * Prepare the map
102 public void init() throws HgRuntimeException { 104 public void init() throws HgRuntimeException {
103 final int revisionCount = revlog.getRevisionCount(); 105 final int revisionCount = revlog.getRevisionCount();
104 firstParent = new Nodeid[revisionCount]; 106 firstParent = new Nodeid[revisionCount];
105 // TODO [post 1.1] Branches/merges are less frequent, and most of secondParent would be -1/null, hence 107 // TODO [post 1.1] Branches/merges are less frequent, and most of secondParent would be -1/null, hence
106 // IntMap might be better alternative here, but need to carefully analyze (test) whether this brings 108 // IntMap might be better alternative here, but need to carefully analyze (test) whether this brings
107 // real improvement (IntMap has 2n capacity, and element lookup is log(n) instead of array's constant) 109 // real improvement (IntMap has 2n capacity, and element lookup is log(n) instead of array's constant).
110 // FWIW: in cpython's repo, with 70k+ revisions, there are 2618 values in secondParent
108 secondParent = new Nodeid[revisionCount]; 111 secondParent = new Nodeid[revisionCount];
109 // 112 //
110 sequential = new Nodeid[revisionCount]; 113 sequential = new Nodeid[revisionCount];
111 sorted = new Nodeid[revisionCount]; 114 sorted = new Nodeid[revisionCount];
112 heads = new BitSet(revisionCount); 115 headsBitSet = new BitSet(revisionCount);
113 revlog.indexWalk(0, TIP, this); 116 revlog.indexWalk(0, TIP, this);
114 Arrays.sort(sorted); 117 Arrays.sort(sorted);
115 // FIXME use ArrayHelper instead 118 // FIXME use ArrayHelper instead
116 sorted2natural = new int[revisionCount]; 119 sorted2natural = new int[revisionCount];
117 for (int i = 0; i < revisionCount; i++) { 120 for (int i = 0; i < revisionCount; i++) {
118 Nodeid n = sequential[i]; 121 Nodeid n = sequential[i];
119 int x = Arrays.binarySearch(sorted, n); 122 int x = Arrays.binarySearch(sorted, n);
120 assertSortedIndex(x); 123 assertSortedIndex(x);
121 sorted2natural[x] = i; 124 sorted2natural[x] = i;
122 } 125 }
126 // no reason to keep BitSet, number of heads is usually small
127 IntMap<Nodeid> _heads = new IntMap<Nodeid>(headsBitSet.size() - headsBitSet.cardinality());
128 int index = 0;
129 while (index < sequential.length) {
130 index = headsBitSet.nextClearBit(index);
131 // nextClearBit(length-1) gives length when bit is set,
132 // however, last revision can't be a parent of any other, and
133 // the last bit would be always 0, and no AIOOBE
134 _heads.put(index, sequential[index]);
135 index++;
136 }
137 headsBitSet = null;
138 heads = _heads;
123 } 139 }
124 140
125 private void assertSortedIndex(int x) { 141 private void assertSortedIndex(int x) {
126 if (x < 0) { 142 if (x < 0) {
127 throw new HgInvalidStateException(String.format("Bad index", x)); 143 throw new HgInvalidStateException(String.format("Bad index", x));
213 } 229 }
214 } 230 }
215 return result; 231 return result;
216 } 232 }
217 233
234 public long AAA = 0;
235
218 /** 236 /**
219 * @return revisions that have supplied revision as their immediate parent 237 * @return revisions that have supplied revision as their immediate parent
220 */ 238 */
221 public List<Nodeid> directChildren(Nodeid nid) { 239 public List<Nodeid> directChildren(Nodeid nid) {
222 int x = Arrays.binarySearch(sorted, nid); 240 int x = Arrays.binarySearch(sorted, nid);
224 nid = sorted[x]; // canonical instance 242 nid = sorted[x]; // canonical instance
225 int start = sorted2natural[x]; 243 int start = sorted2natural[x];
226 if (!hasChildren(start)) { 244 if (!hasChildren(start)) {
227 return Collections.emptyList(); 245 return Collections.emptyList();
228 } 246 }
229 LinkedList<Nodeid> result = new LinkedList<Nodeid>(); 247 ArrayList<Nodeid> result = new ArrayList<Nodeid>(5);
230 for (int i = start + 1; i < sequential.length; i++) { 248 for (int i = start + 1; i < sequential.length; i++) {
249 AAA++;
231 if (nid == firstParent[i] || nid == secondParent[i]) { 250 if (nid == firstParent[i] || nid == secondParent[i]) {
232 result.add(sequential[i]); 251 result.add(sequential[i]);
233 } 252 }
234 } 253 }
235 return result; 254 return result;
289 308
290 /** 309 /**
291 * @return elements of this map that do not have a child recorded therein. 310 * @return elements of this map that do not have a child recorded therein.
292 */ 311 */
293 public Collection<Nodeid> heads() { 312 public Collection<Nodeid> heads() {
294 ArrayList<Nodeid> result = new ArrayList<Nodeid>(); 313 return heads.values();
295 int index = 0;
296 do {
297 index = heads.nextClearBit(index);
298 // nextClearBit(length-1) gives length when bit is set,
299 // however, last revision can't be a parent of any other, and
300 // the last bit would be always 0, and no AIOOBE
301 result.add(sequential[index]);
302 index++;
303 } while (index < sequential.length);
304 return result;
305 } 314 }
306 315
307 private boolean hasChildren(int sequentialIndex) { 316 private boolean hasChildren(int sequentialIndex) {
308 return heads.get(sequentialIndex); 317 return !heads.containsKey(sequentialIndex);
309 } 318 }
310 } 319 }