diff options
-rw-r--r-- | Documentation/config.txt | 11 | ||||
-rw-r--r-- | cache.h | 1 | ||||
-rw-r--r-- | config.c | 10 | ||||
-rw-r--r-- | environment.c | 1 | ||||
-rw-r--r-- | sha1_file.c | 66 |
5 files changed, 76 insertions, 13 deletions
diff --git a/Documentation/config.txt b/Documentation/config.txt index 28fe6942cf..d71653dc65 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -118,6 +118,17 @@ core.legacyheaders:: database directly (where the "http://" and "rsync://" protocols count as direct access). +core.packedGitWindowSize:: + Number of bytes of a pack file to map into memory in a + single mapping operation. Larger window sizes may allow + your system to process a smaller number of large pack files + more quickly. Smaller window sizes will negatively affect + performance due to increased calls to the opreating system's + memory manager, but may improve performance when accessing + a large number of large pack files. Default is 32 MiB, + which should be reasonable for all users/operating systems. + You probably do not need to adjust this value. + core.packedGitLimit:: Maximum number of bytes to map simultaneously into memory from pack files. If Git needs to access more than this many @@ -196,6 +196,7 @@ extern int warn_ambiguous_refs; extern int shared_repository; extern const char *apply_default_whitespace; extern int zlib_compression_level; +extern size_t packed_git_window_size; extern size_t packed_git_limit; #define GIT_REPO_VERSION 0 @@ -298,6 +298,16 @@ int git_default_config(const char *var, const char *value) return 0; } + if (!strcmp(var, "core.packedgitwindowsize")) { + int pgsz = getpagesize(); + packed_git_window_size = git_config_int(var, value); + packed_git_window_size /= pgsz; + if (!packed_git_window_size) + packed_git_window_size = 1; + packed_git_window_size *= pgsz; + return 0; + } + if (!strcmp(var, "core.packedgitlimit")) { packed_git_limit = git_config_int(var, value); return 0; diff --git a/environment.c b/environment.c index a3ddae68a7..e559fd69c7 100644 --- a/environment.c +++ b/environment.c @@ -23,6 +23,7 @@ char *git_log_output_encoding; int shared_repository = PERM_UMASK; const char *apply_default_whitespace; int zlib_compression_level = Z_DEFAULT_COMPRESSION; +size_t packed_git_window_size = 32 * 1024 * 1024; size_t packed_git_limit = 256 * 1024 * 1024; int pager_in_use; int pager_use_color = 1; diff --git a/sha1_file.c b/sha1_file.c index 8e14a5a882..548535c844 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -397,8 +397,9 @@ static char *find_sha1_file(const unsigned char *sha1, struct stat *st) return NULL; } -static int pack_used_ctr; -static unsigned long pack_mapped; +static unsigned int pack_used_ctr; +static size_t pack_mapped; +static size_t page_size; struct packed_git *packed_git; static int check_packed_git_idx(const char *path, unsigned long *idx_size_, @@ -536,31 +537,70 @@ static void open_packed_git(struct packed_git *p) die("packfile %s does not match index", p->pack_name); } +static int in_window(struct pack_window *win, unsigned long offset) +{ + /* We must promise at least 20 bytes (one hash) after the + * offset is available from this window, otherwise the offset + * is not actually in this window and a different window (which + * has that one hash excess) must be used. This is to support + * the object header and delta base parsing routines below. + */ + off_t win_off = win->offset; + return win_off <= offset + && (offset + 20) <= (win_off + win->len); +} + unsigned char* use_pack(struct packed_git *p, struct pack_window **w_cursor, unsigned long offset, unsigned int *left) { - struct pack_window *win = p->windows; + struct pack_window *win = *w_cursor; if (p->pack_fd == -1) open_packed_git(p); - if (!win) { - pack_mapped += p->pack_size; - while (packed_git_limit < pack_mapped && unuse_one_window()) - ; /* nothing */ - win = xcalloc(1, sizeof(*win)); - win->len = p->pack_size; - win->base = mmap(NULL, p->pack_size, PROT_READ, MAP_PRIVATE, p->pack_fd, 0); - if (win->base == MAP_FAILED) - die("packfile %s cannot be mapped.", p->pack_name); - p->windows = win; + + /* Since packfiles end in a hash of their content and its + * pointless to ask for an offset into the middle of that + * hash, and the in_window function above wouldn't match + * don't allow an offset too close to the end of the file. + */ + if (offset > (p->pack_size - 20)) + die("offset beyond end of packfile (truncated pack?)"); + + if (!win || !in_window(win, offset)) { + if (win) + win->inuse_cnt--; + for (win = p->windows; win; win = win->next) { + if (in_window(win, offset)) + break; + } + if (!win) { + if (!page_size) + page_size = getpagesize(); + win = xcalloc(1, sizeof(*win)); + win->offset = (offset / page_size) * page_size; + win->len = p->pack_size - win->offset; + if (win->len > packed_git_window_size) + win->len = packed_git_window_size; + pack_mapped += win->len; + while (packed_git_limit < pack_mapped && unuse_one_window()) + ; /* nothing */ + win->base = mmap(NULL, win->len, + PROT_READ, MAP_PRIVATE, + p->pack_fd, win->offset); + if (win->base == MAP_FAILED) + die("packfile %s cannot be mapped.", p->pack_name); + win->next = p->windows; + p->windows = win; + } } if (win != *w_cursor) { win->last_used = pack_used_ctr++; win->inuse_cnt++; *w_cursor = win; } + offset -= win->offset; if (left) *left = win->len - offset; return win->base + offset; |