Skip to content

Commit

Permalink
index-pack: Honor core.deltaBaseCacheLimit when resolving deltas
Browse files Browse the repository at this point in the history
If we are trying to resolve deltas for a long delta chain composed
of multi-megabyte objects we can easily run into requiring 500M+
of memory to hold each object in the chain on the call stack while
we recurse into the dependent objects and resolve them.

We now use a simple delta cache that discards objects near the
bottom of the call stack first, as they are the most least recently
used objects in this current delta chain.  If we recurse out of a
chain we may find the base object is no longer available, as it was
free'd to keep memory under the deltaBaseCacheLimit.  In such cases
we must unpack the base object again, which will require recursing
back to the root of the top of the delta chain as we released that
root first.

The astute reader will probably realize that we can still exceed
the delta base cache limit, but this happens only if the most
recent base plus the delta plus the inflated dependent sum up to
more than the base cache limit.  Due to the way patch_delta is
currently implemented we cannot operate in less memory anyway.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
Shawn O. Pearce authored and Junio C Hamano committed Jul 15, 2008
1 parent 03993e1 commit 92392b4
Showing 1 changed file with 46 additions and 2 deletions.
48 changes: 46 additions & 2 deletions index-pack.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ struct delta_entry
static struct object_entry *objects;
static struct delta_entry *deltas;
static struct base_data *base_cache;
static size_t base_cache_used;
static int nr_objects;
static int nr_deltas;
static int nr_resolved_deltas;
Expand Down Expand Up @@ -219,6 +220,20 @@ static void bad_object(unsigned long offset, const char *format, ...)
die("pack has bad object at offset %lu: %s", offset, buf);
}

static void prune_base_data(struct base_data *retain)
{
struct base_data *b = base_cache;
for (b = base_cache;
base_cache_used > delta_base_cache_limit && b;
b = b->child) {
if (b->data && b != retain) {
free(b->data);
b->data = NULL;
base_cache_used -= b->size;
}
}
}

static void link_base_data(struct base_data *base, struct base_data *c)
{
if (base)
Expand All @@ -228,6 +243,8 @@ static void link_base_data(struct base_data *base, struct base_data *c)

c->base = base;
c->child = NULL;
base_cache_used += c->size;
prune_base_data(c);
}

static void unlink_base_data(struct base_data *c)
Expand All @@ -237,7 +254,10 @@ static void unlink_base_data(struct base_data *c)
base->child = NULL;
else
base_cache = NULL;
free(c->data);
if (c->data) {
free(c->data);
base_cache_used -= c->size;
}
}

static void *unpack_entry_data(unsigned long offset, unsigned long size)
Expand Down Expand Up @@ -455,6 +475,30 @@ static void sha1_object(const void *data, unsigned long size,
}
}

static void *get_base_data(struct base_data *c)
{
if (!c->data) {
struct object_entry *obj = c->obj;

if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA) {
void *base = get_base_data(c->base);
void *raw = get_data_from_pack(obj);
c->data = patch_delta(
base, c->base->size,
raw, obj->size,
&c->size);
free(raw);
if (!c->data)
bad_object(obj->idx.offset, "failed to apply delta");
} else
c->data = get_data_from_pack(obj);

base_cache_used += c->size;
prune_base_data(c);
}
return c->data;
}

static void resolve_delta(struct object_entry *delta_obj,
struct base_data *base_obj, enum object_type type)
{
Expand All @@ -467,7 +511,7 @@ static void resolve_delta(struct object_entry *delta_obj,
delta_obj->real_type = type;
delta_data = get_data_from_pack(delta_obj);
delta_size = delta_obj->size;
result.data = patch_delta(base_obj->data, base_obj->size,
result.data = patch_delta(get_base_data(base_obj), base_obj->size,
delta_data, delta_size,
&result.size);
free(delta_data);
Expand Down

0 comments on commit 92392b4

Please sign in to comment.