Limit the size of the new delta_base_cache
authorShawn O. Pearce <spearce@spearce.org>
Mon, 19 Mar 2007 05:14:37 +0000 (01:14 -0400)
committerJunio C Hamano <junkio@cox.net>
Mon, 19 Mar 2007 05:43:37 +0000 (22:43 -0700)
The new configuration variable core.deltaBaseCacheLimit allows the
user to control how much memory they are willing to give to Git for
caching base objects of deltas. This is not normally meant to be
a user tweakable knob; the "out of the box" settings are meant to
be suitable for almost all workloads.

We default to 16 MiB under the assumption that the cache is not
meant to consume all of the user's available memory, and that the
cache's main purpose was to cache trees, for faster path limiters
during revision traversal. Since trees tend to be relatively small
objects, this relatively small limit should still allow a large
number of objects.

On the other hand we don't want the cache to start storing 200
different versions of a 200 MiB blob, as this could easily blow
the entire address space of a 32 bit process.

We evict OBJ_BLOB from the cache first (credit goes to Junio) as
we want to favor OBJ_TREE within the cache. These are the objects
that have the highest inflate() startup penalty, as they tend to
be small and thus don't have that much of a chance to ammortize
that penalty over the entire data.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
Documentation/config.txt
cache.h
config.c
environment.c
sha1_file.c
index 66886424bb6c30f1829482230ff3da4810461f22..cf1e040381a99ed458dc061fe103e3c2d2c9474c 100644 (file)
@@ -240,6 +240,19 @@ the largest projects.  You probably do not need to adjust this value.
 +
 Common unit suffixes of 'k', 'm', or 'g' are supported.
 
+core.deltaBaseCacheLimit::
+       Maximum number of bytes to reserve for caching base objects
+       that multiple deltafied objects reference.  By storing the
+       entire decompressed base objects in a cache Git is able
+       to avoid unpacking and decompressing frequently used base
+       objects multiple times.
++
+Default is 16 MiB on all platforms.  This should be reasonable
+for all users/operating systems, except on the largest projects.
+You probably do not need to adjust this value.
++
+Common unit suffixes of 'k', 'm', or 'g' are supported.
+
 alias.*::
        Command aliases for the gitlink:git[1] command wrapper - e.g.
        after defining "alias.last = cat-file commit HEAD", the invocation
diff --git a/cache.h b/cache.h
index 5396d3366dd5d802a4e3ce43d296957c94ddb028..1b50a742a399b37f45724e64504028233f48f7a8 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -228,6 +228,7 @@ extern const char *apply_default_whitespace;
 extern int zlib_compression_level;
 extern size_t packed_git_window_size;
 extern size_t packed_git_limit;
+extern size_t delta_base_cache_limit;
 extern int auto_crlf;
 
 #define GIT_REPO_VERSION 0
index d537311ae1d4321c3f5f90930f7451262caf2ae5..6479855723d6dc94fa7c440868724a794bb59901 100644 (file)
--- a/config.c
+++ b/config.c
@@ -331,6 +331,11 @@ int git_default_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.deltabasecachelimit")) {
+               delta_base_cache_limit = git_config_int(var, value);
+               return 0;
+       }
+
        if (!strcmp(var, "core.autocrlf")) {
                if (value && !strcasecmp(value, "input")) {
                        auto_crlf = -1;
index fff4a4da50d1054dd3989d40ec5310ea7258f97c..22316597df60648c850001e068938eaf39d57f6d 100644 (file)
@@ -27,6 +27,7 @@ const char *apply_default_whitespace;
 int zlib_compression_level = Z_DEFAULT_COMPRESSION;
 size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
 size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
+size_t delta_base_cache_limit = 16 * 1024 * 1024;
 int pager_in_use;
 int pager_use_color = 1;
 int auto_crlf = 0;     /* 1: both ways, -1: only when adding git objects */
index ee64865b607c70ff42f851b91c19316ef90ce7b9..b0b21776e79cddcdbbf93b4ee6daed2abdf804c4 100644 (file)
@@ -1354,6 +1354,7 @@ static void *unpack_compressed_entry(struct packed_git *p,
 
 #define MAX_DELTA_CACHE (256)
 
+static size_t delta_base_cached;
 static struct delta_base_cache_entry {
        struct packed_git *p;
        off_t base_offset;
@@ -1384,8 +1385,10 @@ static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset,
        return unpack_entry(p, base_offset, type, base_size);
 
 found_cache_entry:
-       if (!keep_cache)
+       if (!keep_cache) {
                ent->data = NULL;
+               delta_base_cached -= ent->size;
+       }
        else {
                ret = xmalloc(ent->size + 1);
                memcpy(ret, ent->data, ent->size);
@@ -1396,14 +1399,33 @@ static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset,
        return ret;
 }
 
+static inline void release_delta_base_cache(struct delta_base_cache_entry *ent)
+{
+       if (ent->data) {
+               free(ent->data);
+               ent->data = NULL;
+               delta_base_cached -= ent->size;
+       }
+}
+
 static void add_delta_base_cache(struct packed_git *p, off_t base_offset,
        void *base, unsigned long base_size, enum object_type type)
 {
-       unsigned long hash = pack_entry_hash(p, base_offset);
+       unsigned long i, hash = pack_entry_hash(p, base_offset);
        struct delta_base_cache_entry *ent = delta_base_cache + hash;
 
-       if (ent->data)
-               free(ent->data);
+       release_delta_base_cache(ent);
+       delta_base_cached += base_size;
+       for (i = 0; delta_base_cached > delta_base_cache_limit
+               && i < ARRAY_SIZE(delta_base_cache); i++) {
+               struct delta_base_cache_entry *f = delta_base_cache + i;
+               if (f->type == OBJ_BLOB)
+                       release_delta_base_cache(f);
+       }
+       for (i = 0; delta_base_cached > delta_base_cache_limit
+               && i < ARRAY_SIZE(delta_base_cache); i++)
+               release_delta_base_cache(delta_base_cache + i);
+
        ent->p = p;
        ent->base_offset = base_offset;
        ent->type = type;