DirCacheBuilder: Speed up reading from trees

Recursively copying a tree into a DirCache is a bottleneck for some
algorithms like the in memory merge code in Gerrit Code Review.  Drop
a layer down in the stack and use CanonicalTreeParser directly as the
addition logic only processes 1 tree at a time and does not need the
merge sorting feature (or overhead) of TreeWalk.

Combined with 761814fe9c ("DirCacheEntry: Speed up creation by
avoiding string cast") tree loading 38,900 entries nearly halves
in running time from 70ms to 36ms on some platforms.

Change-Id: If1490ca25de0679a71cf508f59b486f9cc816165
This commit is contained in:
Shawn Pearce 2015-11-28 09:23:59 -08:00
parent b0eb744604
commit 2d011cd648
2 changed files with 53 additions and 19 deletions

View File

@ -44,6 +44,9 @@
package org.eclipse.jgit.dircache; package org.eclipse.jgit.dircache;
import static org.eclipse.jgit.lib.FileMode.TYPE_MASK;
import static org.eclipse.jgit.lib.FileMode.TYPE_TREE;
import java.io.IOException; import java.io.IOException;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.Arrays; import java.util.Arrays;
@ -51,9 +54,7 @@
import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.CanonicalTreeParser; import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.TreeWalk;
/** /**
* Updates a {@link DirCache} by adding individual {@link DirCacheEntry}s. * Updates a {@link DirCache} by adding individual {@link DirCacheEntry}s.
@ -163,27 +164,56 @@ public void keep(final int pos, int cnt) {
* @throws IOException * @throws IOException
* a tree cannot be read to iterate through its entries. * a tree cannot be read to iterate through its entries.
*/ */
public void addTree(final byte[] pathPrefix, final int stage, public void addTree(byte[] pathPrefix, int stage, ObjectReader reader,
final ObjectReader reader, final AnyObjectId tree) throws IOException { AnyObjectId tree) throws IOException {
final TreeWalk tw = new TreeWalk(reader); CanonicalTreeParser p = createTreeParser(pathPrefix, reader, tree);
tw.addTree(new CanonicalTreeParser(pathPrefix, reader, tree while (!p.eof()) {
.toObjectId())); if (isTree(p)) {
tw.setRecursive(true); p = enterTree(p, reader);
if (tw.next()) { continue;
final DirCacheEntry newEntry = toEntry(stage, tw); }
beforeAdd(newEntry);
fastAdd(newEntry); DirCacheEntry first = toEntry(stage, p);
while (tw.next()) beforeAdd(first);
fastAdd(toEntry(stage, tw)); fastAdd(first);
p = p.next();
break;
}
// Rest of tree entries are correctly sorted; use fastAdd().
while (!p.eof()) {
if (isTree(p)) {
p = enterTree(p, reader);
} else {
fastAdd(toEntry(stage, p));
p = p.next();
}
} }
} }
private DirCacheEntry toEntry(final int stage, final TreeWalk tw) { private static CanonicalTreeParser createTreeParser(byte[] pathPrefix,
final DirCacheEntry e = new DirCacheEntry(tw.getRawPath(), stage); ObjectReader reader, AnyObjectId tree) throws IOException {
final AbstractTreeIterator i; return new CanonicalTreeParser(pathPrefix, reader, tree);
}
i = tw.getTree(0, AbstractTreeIterator.class); private static boolean isTree(CanonicalTreeParser p) {
e.setFileMode(tw.getFileMode(0)); return (p.getEntryRawMode() & TYPE_MASK) == TYPE_TREE;
}
private static CanonicalTreeParser enterTree(CanonicalTreeParser p,
ObjectReader reader) throws IOException {
p = p.createSubtreeIterator(reader);
return p.eof() ? p.next() : p;
}
private static DirCacheEntry toEntry(int stage, CanonicalTreeParser i) {
byte[] buf = i.getEntryPathBuffer();
int len = i.getEntryPathLength();
byte[] path = new byte[len];
System.arraycopy(buf, 0, path, 0, len);
DirCacheEntry e = new DirCacheEntry(path, stage);
e.setFileMode(i.getEntryRawMode());
e.setObjectIdFromRaw(i.idBuffer(), i.idOffset()); e.setObjectIdFromRaw(i.idBuffer(), i.idOffset());
return e; return e;
} }

View File

@ -505,6 +505,10 @@ public void setFileMode(final FileMode mode) {
NB.encodeInt32(info, infoOffset + P_MODE, mode.getBits()); NB.encodeInt32(info, infoOffset + P_MODE, mode.getBits());
} }
void setFileMode(int mode) {
NB.encodeInt32(info, infoOffset + P_MODE, mode);
}
/** /**
* Get the cached creation time of this file, in milliseconds. * Get the cached creation time of this file, in milliseconds.
* *