Browse Source
PackWriter now caches small deltas, or deltas that are very tiny compared to their source inputs, so that the writing phase goes faster by reusing those cached deltas. The cached data is stored compressed, which usually translates to a bigger footprint due to deltas being very hard to compress, but saves time during writing by avoiding the deflate step. They are held under SoftReferences so that the JVM GC can clear out deltas if memory gets very tight. We would rather continue working and spend a bit more CPU time during writing than crash due to OOME. To avoid OutOfMemoryErrors during the caching phase we also trap OOME and just abort out of the caching. Because deflateBound() always produces something larger than what we need to actually store the deflated data, we copy it over into a new buffer if the actual length doesn't match the buffer length. When packing jgit.git this saves over 111 KiB in the cache, and is thus a worthwhile hit on CPU time. To further save memory we store the inflated size of the delta (which we need for the object header) in the same field as the pathHash, as the pathHash is no longer necessary by this phase of the packing algorithm. Change-Id: I0da0c600d845e8ec962289751f24e65b5afa56d7 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>stable-0.9
Shawn O. Pearce
15 years ago
5 changed files with 379 additions and 21 deletions
@ -0,0 +1,129 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2010, Google Inc. |
||||||
|
* and other copyright owners as documented in the project's IP log. |
||||||
|
* |
||||||
|
* This program and the accompanying materials are made available |
||||||
|
* under the terms of the Eclipse Distribution License v1.0 which |
||||||
|
* accompanies this distribution, is reproduced below, and is |
||||||
|
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||||
|
* |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or |
||||||
|
* without modification, are permitted provided that the following |
||||||
|
* conditions are met: |
||||||
|
* |
||||||
|
* - Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* - Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following |
||||||
|
* disclaimer in the documentation and/or other materials provided |
||||||
|
* with the distribution. |
||||||
|
* |
||||||
|
* - Neither the name of the Eclipse Foundation, Inc. nor the |
||||||
|
* names of its contributors may be used to endorse or promote |
||||||
|
* products derived from this software without specific prior |
||||||
|
* written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
||||||
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.eclipse.jgit.storage.pack; |
||||||
|
|
||||||
|
import java.lang.ref.ReferenceQueue; |
||||||
|
import java.lang.ref.SoftReference; |
||||||
|
|
||||||
|
class DeltaCache { |
||||||
|
private final long size; |
||||||
|
|
||||||
|
private final int entryLimit; |
||||||
|
|
||||||
|
private final ReferenceQueue<byte[]> queue; |
||||||
|
|
||||||
|
private long used; |
||||||
|
|
||||||
|
DeltaCache(PackWriter pw) { |
||||||
|
size = pw.getDeltaCacheSize(); |
||||||
|
entryLimit = pw.getDeltaCacheLimit(); |
||||||
|
queue = new ReferenceQueue<byte[]>(); |
||||||
|
} |
||||||
|
|
||||||
|
boolean canCache(int length, ObjectToPack src, ObjectToPack res) { |
||||||
|
// If the cache would overflow, don't store.
|
||||||
|
//
|
||||||
|
if (0 < size && size < used + length) { |
||||||
|
checkForGarbageCollectedObjects(); |
||||||
|
if (0 < size && size < used + length) |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (length < entryLimit) { |
||||||
|
used += length; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
// If the combined source files are multiple megabytes but the delta
|
||||||
|
// is on the order of a kilobyte or two, this was likely costly to
|
||||||
|
// construct. Cache it anyway, even though its over the limit.
|
||||||
|
//
|
||||||
|
if (length >> 10 < (src.getWeight() >> 20) + (res.getWeight() >> 21)) { |
||||||
|
used += length; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
void credit(int reservedSize) { |
||||||
|
used -= reservedSize; |
||||||
|
} |
||||||
|
|
||||||
|
Ref cache(byte[] data, int actLen, int reservedSize) { |
||||||
|
// The caller may have had to allocate more space than is
|
||||||
|
// required. If we are about to waste anything, shrink it.
|
||||||
|
//
|
||||||
|
if (data.length != actLen) { |
||||||
|
byte[] nbuf = new byte[actLen]; |
||||||
|
System.arraycopy(data, 0, nbuf, 0, actLen); |
||||||
|
data = nbuf; |
||||||
|
} |
||||||
|
|
||||||
|
// When we reserved space for this item we did it for the
|
||||||
|
// inflated size of the delta, but we were just given the
|
||||||
|
// compressed version. Adjust the cache cost to match.
|
||||||
|
//
|
||||||
|
if (reservedSize != data.length) { |
||||||
|
used -= reservedSize; |
||||||
|
used += data.length; |
||||||
|
} |
||||||
|
return new Ref(data, queue); |
||||||
|
} |
||||||
|
|
||||||
|
private void checkForGarbageCollectedObjects() { |
||||||
|
Ref r; |
||||||
|
while ((r = (Ref) queue.poll()) != null) |
||||||
|
used -= r.cost; |
||||||
|
} |
||||||
|
|
||||||
|
static class Ref extends SoftReference<byte[]> { |
||||||
|
final int cost; |
||||||
|
|
||||||
|
Ref(byte[] array, ReferenceQueue<byte[]> queue) { |
||||||
|
super(array, queue); |
||||||
|
cost = array.length; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue