Browse Source

Tighten object header writing in PackOutuptStream

Most objects are written as OFS_DELTA with the base in the pack,
that is why this case comes first in writeHeader(). Rewrite the
condition to always examine this first and cache the PackWriter's
formatting flag for use of OFS_DELTA headers, in modern Git networks
this is true more often then it it is false.

Assume the cost of write() is high, especially due to entering the
MessageDigest to update the pack footer SHA-1 computation. Combine
the OFS_DELTA information as part of the header buffer so that the
entire burst is a single write call, rather than two relatively
small ones. Most OFS_DELTA headers are <= 6 bytes, so this rewrite
tranforms 2 writes of 3 bytes each into 1 write of ~6 bytes.

Try to simplify the objectHeader code to reduce branches and use
more local registers. This shouldn't really be necessary if the
compiler is well optimized, but it isn't very hard to clarify data
usage to either javac or the JIT, which may make it easier for the
JIT to produce better machine code for this method.

Change-Id: I2b12788ad6866076fabbf7fa11f8cce44e963f35
stable-3.0
Shawn Pearce 12 years ago
parent
commit
46ef61a702
  1. 71
      org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackOutputStream.java

71
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackOutputStream.java

@ -44,6 +44,10 @@
package org.eclipse.jgit.internal.storage.pack; package org.eclipse.jgit.internal.storage.pack;
import static org.eclipse.jgit.lib.Constants.OBJ_OFS_DELTA;
import static org.eclipse.jgit.lib.Constants.OBJ_REF_DELTA;
import static org.eclipse.jgit.lib.Constants.PACK_SIGNATURE;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.security.MessageDigest; import java.security.MessageDigest;
@ -73,6 +77,8 @@ public final class PackOutputStream extends OutputStream {
private long checkCancelAt; private long checkCancelAt;
private boolean ofsDelta;
/** /**
* Initialize a pack output stream. * Initialize a pack output stream.
* <p> * <p>
@ -132,10 +138,11 @@ public final class PackOutputStream extends OutputStream {
final void writeFileHeader(int version, long objectCount) final void writeFileHeader(int version, long objectCount)
throws IOException { throws IOException {
System.arraycopy(Constants.PACK_SIGNATURE, 0, headerBuffer, 0, 4); System.arraycopy(PACK_SIGNATURE, 0, headerBuffer, 0, 4);
NB.encodeInt32(headerBuffer, 4, version); NB.encodeInt32(headerBuffer, 4, version);
NB.encodeInt32(headerBuffer, 8, (int) objectCount); NB.encodeInt32(headerBuffer, 8, (int) objectCount);
write(headerBuffer, 0, 12); write(headerBuffer, 0, 12);
ofsDelta = packWriter.isDeltaBaseAsOffset();
} }
/** /**
@ -175,43 +182,45 @@ public final class PackOutputStream extends OutputStream {
*/ */
public final void writeHeader(ObjectToPack otp, long rawLength) public final void writeHeader(ObjectToPack otp, long rawLength)
throws IOException { throws IOException {
if (otp.isDeltaRepresentation()) { ObjectToPack b = otp.getDeltaBase();
if (packWriter.isDeltaBaseAsOffset()) { if (b != null && (b.isWritten() & ofsDelta)) {
ObjectToPack baseInPack = otp.getDeltaBase(); int n = objectHeader(rawLength, OBJ_OFS_DELTA, headerBuffer);
if (baseInPack != null && baseInPack.isWritten()) { n = ofsDelta(count - b.getOffset(), headerBuffer, n);
final long start = count; write(headerBuffer, 0, n);
int n = encodeTypeSize(Constants.OBJ_OFS_DELTA, rawLength); } else if (otp.isDeltaRepresentation()) {
write(headerBuffer, 0, n); int n = objectHeader(rawLength, OBJ_REF_DELTA, headerBuffer);
long offsetDiff = start - baseInPack.getOffset();
n = headerBuffer.length - 1;
headerBuffer[n] = (byte) (offsetDiff & 0x7F);
while ((offsetDiff >>= 7) > 0)
headerBuffer[--n] = (byte) (0x80 | (--offsetDiff & 0x7F));
write(headerBuffer, n, headerBuffer.length - n);
return;
}
}
int n = encodeTypeSize(Constants.OBJ_REF_DELTA, rawLength);
otp.getDeltaBaseId().copyRawTo(headerBuffer, n); otp.getDeltaBaseId().copyRawTo(headerBuffer, n);
write(headerBuffer, 0, n + Constants.OBJECT_ID_LENGTH); write(headerBuffer, 0, n + 20);
} else { } else {
int n = encodeTypeSize(otp.getType(), rawLength); int n = objectHeader(rawLength, otp.getType(), headerBuffer);
write(headerBuffer, 0, n); write(headerBuffer, 0, n);
} }
} }
private final int encodeTypeSize(int type, long rawLength) { private static final int objectHeader(long len, int type, byte[] buf) {
long nextLength = rawLength >>> 4; byte b = (byte) ((type << 4) | (len & 0x0F));
headerBuffer[0] = (byte) ((nextLength > 0 ? 0x80 : 0x00) | (type << 4) | (rawLength & 0x0F)); int n = 0;
rawLength = nextLength; for (len >>>= 4; len != 0; len >>>= 7) {
int n = 1; buf[n++] = (byte) (0x80 | b);
while (rawLength > 0) { b = (byte) (len & 0x7F);
nextLength >>>= 7;
headerBuffer[n++] = (byte) ((nextLength > 0 ? 0x80 : 0x00) | (rawLength & 0x7F));
rawLength = nextLength;
} }
buf[n++] = b;
return n;
}
private static final int ofsDelta(long diff, byte[] buf, int p) {
p += ofsDeltaVarIntLength(diff);
int n = p;
buf[--n] = (byte) (diff & 0x7F);
while ((diff >>>= 7) != 0)
buf[--n] = (byte) (0x80 | (--diff & 0x7F));
return p;
}
private static final int ofsDeltaVarIntLength(long v) {
int n = 1;
for (; (v >>>= 7) != 0; n++)
--v;
return n; return n;
} }

Loading…
Cancel
Save