Browse Source

Make DeltaBaseCache per-ObjectReader

The 'Counting objects' phase of PackWriter requires good hit rates
from the DeltaBaseCache while walking trees, the deltas need to find
their bases in the cache in order to inflate in a reasonable time.

If JGit is running in a multi-threaded server, such as Gerrit Code
Review, each thread needs its own DeltaBaseCache to prevent one thread
from evicting the other thread's relevant bases.  Move the cache to be
per-ObjectReader, lazily allocated when required by a PackFile.

Change-Id: If9d5ed06728e813632ae96dcfb811f4860b276e8
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
stable-0.12
Shawn O. Pearce 14 years ago
parent
commit
53fb027284
  1. 65
      org.eclipse.jgit/src/org/eclipse/jgit/storage/file/DeltaBaseCache.java
  2. 7
      org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackFile.java
  3. 9
      org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCursor.java

65
org.eclipse.jgit/src/org/eclipse/jgit/storage/file/DeltaBaseCache.java

@ -54,35 +54,36 @@ class DeltaBaseCache {
return (((int) position) << 22) >>> 22; return (((int) position) << 22) >>> 22;
} }
private static int maxByteCount; private static volatile int defaultMaxByteCount;
private static final Slot[] cache; private final int maxByteCount;
private static Slot lruHead; private final Slot[] cache;
private static Slot lruTail; private Slot lruHead;
private static int openByteCount; private Slot lruTail;
private int openByteCount;
static { static {
DEAD = new SoftReference<Entry>(null); DEAD = new SoftReference<Entry>(null);
maxByteCount = new WindowCacheConfig().getDeltaBaseCacheLimit(); reconfigure(new WindowCacheConfig());
cache = new Slot[CACHE_SZ];
for (int i = 0; i < CACHE_SZ; i++)
cache[i] = new Slot();
} }
static synchronized void reconfigure(final WindowCacheConfig cfg) { static void reconfigure(WindowCacheConfig cfg) {
final int dbLimit = cfg.getDeltaBaseCacheLimit(); defaultMaxByteCount = cfg.getDeltaBaseCacheLimit();
if (maxByteCount != dbLimit) {
maxByteCount = dbLimit;
releaseMemory();
} }
DeltaBaseCache() {
maxByteCount = defaultMaxByteCount;
cache = new Slot[CACHE_SZ];
} }
static synchronized Entry get(final PackFile pack, final long position) { Entry get(final PackFile pack, final long position) {
final Slot e = cache[hash(position)]; Slot e = cache[hash(position)];
if (e == null)
return null;
if (e.provider == pack && e.position == position) { if (e.provider == pack && e.position == position) {
final Entry buf = e.data.get(); final Entry buf = e.data.get();
if (buf != null) { if (buf != null) {
@ -93,13 +94,18 @@ class DeltaBaseCache {
return null; return null;
} }
static synchronized void store(final PackFile pack, final long position, void store(final PackFile pack, final long position,
final byte[] data, final int objectType) { final byte[] data, final int objectType) {
if (data.length > maxByteCount) if (data.length > maxByteCount)
return; // Too large to cache. return; // Too large to cache.
final Slot e = cache[hash(position)]; Slot e = cache[hash(position)];
if (e == null) {
e = new Slot();
cache[hash(position)] = e;
} else {
clearEntry(e); clearEntry(e);
}
openByteCount += data.length; openByteCount += data.length;
releaseMemory(); releaseMemory();
@ -111,7 +117,7 @@ class DeltaBaseCache {
moveToHead(e); moveToHead(e);
} }
private static void releaseMemory() { private void releaseMemory() {
while (openByteCount > maxByteCount && lruTail != null) { while (openByteCount > maxByteCount && lruTail != null) {
final Slot currOldest = lruTail; final Slot currOldest = lruTail;
final Slot nextOldest = currOldest.lruPrev; final Slot nextOldest = currOldest.lruPrev;
@ -128,16 +134,7 @@ class DeltaBaseCache {
} }
} }
static synchronized void purge(final PackFile file) { private void moveToHead(final Slot e) {
for (final Slot e : cache) {
if (e.provider == file) {
clearEntry(e);
unlink(e);
}
}
}
private static void moveToHead(final Slot e) {
unlink(e); unlink(e);
e.lruPrev = null; e.lruPrev = null;
e.lruNext = lruHead; e.lruNext = lruHead;
@ -148,7 +145,7 @@ class DeltaBaseCache {
lruHead = e; lruHead = e;
} }
private static void unlink(final Slot e) { private void unlink(final Slot e) {
final Slot prev = e.lruPrev; final Slot prev = e.lruPrev;
final Slot next = e.lruNext; final Slot next = e.lruNext;
if (prev != null) if (prev != null)
@ -157,17 +154,13 @@ class DeltaBaseCache {
next.lruPrev = prev; next.lruPrev = prev;
} }
private static void clearEntry(final Slot e) { private void clearEntry(final Slot e) {
openByteCount -= e.sz; openByteCount -= e.sz;
e.provider = null; e.provider = null;
e.data = DEAD; e.data = DEAD;
e.sz = 0; e.sz = 0;
} }
private DeltaBaseCache() {
throw new UnsupportedOperationException();
}
static class Entry { static class Entry {
final byte[] data; final byte[] data;

7
org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackFile.java

@ -236,7 +236,6 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
* Close the resources utilized by this repository * Close the resources utilized by this repository
*/ */
public void close() { public void close() {
DeltaBaseCache.purge(this);
WindowCache.purge(this); WindowCache.purge(this);
synchronized (this) { synchronized (this) {
loadedIdx = null; loadedIdx = null;
@ -723,7 +722,7 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
if (sz != delta.deltaSize) if (sz != delta.deltaSize)
break SEARCH; break SEARCH;
DeltaBaseCache.Entry e = DeltaBaseCache.get(this, base); DeltaBaseCache.Entry e = curs.getDeltaBaseCache().get(this, base);
if (e != null) { if (e != null) {
type = e.type; type = e.type;
data = e.data; data = e.data;
@ -741,7 +740,7 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
if (sz != delta.deltaSize) if (sz != delta.deltaSize)
break SEARCH; break SEARCH;
DeltaBaseCache.Entry e = DeltaBaseCache.get(this, base); DeltaBaseCache.Entry e = curs.getDeltaBaseCache().get(this, base);
if (e != null) { if (e != null) {
type = e.type; type = e.type;
data = e.data; data = e.data;
@ -769,7 +768,7 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
if (cached) if (cached)
cached = false; cached = false;
else if (delta.next == null) else if (delta.next == null)
DeltaBaseCache.store(this, delta.basePos, data, type); curs.getDeltaBaseCache().store(this, delta.basePos, data, type);
pos = delta.deltaPos; pos = delta.deltaPos;

9
org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCursor.java

@ -83,12 +83,20 @@ final class WindowCursor extends ObjectReader implements ObjectReuseAsIs {
private ByteWindow window; private ByteWindow window;
private DeltaBaseCache baseCache;
final FileObjectDatabase db; final FileObjectDatabase db;
WindowCursor(FileObjectDatabase db) { WindowCursor(FileObjectDatabase db) {
this.db = db; this.db = db;
} }
DeltaBaseCache getDeltaBaseCache() {
if (baseCache == null)
baseCache = new DeltaBaseCache();
return baseCache;
}
@Override @Override
public ObjectReader newReader() { public ObjectReader newReader() {
return new WindowCursor(db); return new WindowCursor(db);
@ -334,6 +342,7 @@ final class WindowCursor extends ObjectReader implements ObjectReuseAsIs {
/** Release the current window cursor. */ /** Release the current window cursor. */
public void release() { public void release() {
window = null; window = null;
baseCache = null;
try { try {
InflaterCache.release(inf); InflaterCache.release(inf);
} finally { } finally {

Loading…
Cancel
Save