From 4d703a1a9016cd0a08994ddf7fc2f4739f223112 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 23 Dec 2006 02:33:25 -0500 Subject: Replace unpack_entry_gently with unpack_entry. The unpack_entry_gently function currently has only two callers: the delta base resolution in sha1_file.c and the main loop of pack-check.c. Both of these must change to using unpack_entry directly when we implement sliding window mmap logic, so I'm doing it earlier to help break down the change set. This may cause a slight performance decrease for delta base resolution as well as for pack-check.c's verify_packfile(), as the pack use counter will be incremented and decremented for every object that is unpacked. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- cache.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'cache.h') diff --git a/cache.h b/cache.h index 29dd290c92..e0a26e9d04 100644 --- a/cache.h +++ b/cache.h @@ -394,7 +394,7 @@ extern struct packed_git *add_packed_git(char *, int, int); extern int num_packed_objects(const struct packed_git *p); extern int nth_packed_object_sha1(const struct packed_git *, int, unsigned char*); extern unsigned long find_pack_entry_one(const unsigned char *, struct packed_git *); -extern void *unpack_entry_gently(struct packed_git *, unsigned long, char *, unsigned long *); +extern void *unpack_entry(struct packed_git *, unsigned long, char *, unsigned long *); extern unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep); extern void packed_object_info_detail(struct packed_git *, unsigned long, char *, unsigned long *, unsigned long *, unsigned int *, unsigned char *); -- cgit v1.2.3 From 77ccc5bbd1bd403abd5f552be7210073bea856a6 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 23 Dec 2006 02:33:35 -0500 Subject: Introduce new config option for mmap limit. Rather than hardcoding the maximum number of bytes which can be mmapped from pack files we should make this value configurable, allowing the end user to increase or decrease this limit on a per-repository basis depending on the size of the repository and the capabilities of their operating system. In general users should not need to manually tune such a low-level setting within the core code, but being able to artifically limit the number of bytes which we can mmap at once from pack files will make it easier to craft test cases for the new mmap sliding window implementation. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- cache.h | 1 + 1 file changed, 1 insertion(+) (limited to 'cache.h') diff --git a/cache.h b/cache.h index e0a26e9d04..816239bea0 100644 --- a/cache.h +++ b/cache.h @@ -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_limit; #define GIT_REPO_VERSION 0 extern int repository_format_version; -- cgit v1.2.3 From c41ee586dc95b757cdff4deae10a30a691ba758b Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 23 Dec 2006 02:33:44 -0500 Subject: Refactor packed_git to prepare for sliding mmap windows. The idea behind the sliding mmap window pack reader implementation is to have multiple mmap regions active against the same pack file, thereby allowing the process to mmap in only the active/hot sections of the pack and reduce overall virtual address space usage. To implement this we need to refactor the mmap related data (pack_base, pack_use_cnt) out of struct packed_git and move them into a new struct pack_window. We are refactoring the code to support a single struct pack_window per packfile, thereby emulating the prior behavior of mmap'ing the entire pack file. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- cache.h | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'cache.h') diff --git a/cache.h b/cache.h index 816239bea0..ae7bceca50 100644 --- a/cache.h +++ b/cache.h @@ -336,14 +336,21 @@ extern struct alternate_object_database { } *alt_odb_list; extern void prepare_alt_odb(void); +struct pack_window { + struct pack_window *next; + unsigned char *base; + off_t offset; + size_t len; + unsigned int last_used; + unsigned int inuse_cnt; +}; + extern struct packed_git { struct packed_git *next; unsigned long index_size; unsigned long pack_size; + struct pack_window *windows; unsigned int *index_base; - void *pack_base; - unsigned int pack_last_used; - unsigned int pack_use_cnt; int pack_local; unsigned char sha1[20]; /* something like ".git/objects/pack/xxxxx.pack" */ -- cgit v1.2.3 From 2dc3a234094afc61c367be3e6018722cd9d92ea9 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 23 Dec 2006 02:33:47 -0500 Subject: Use off_t for index and pack file lengths. Since the index_size and pack_size members of struct packed_git are the lengths of those corresponding files we should use the off_t size of the operating system to store these file lengths, rather than an unsigned long. This would help in the future should we ever resurrect Junio's 64 bit index implementation. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- cache.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'cache.h') diff --git a/cache.h b/cache.h index ae7bceca50..cc872440ef 100644 --- a/cache.h +++ b/cache.h @@ -347,10 +347,10 @@ struct pack_window { extern struct packed_git { struct packed_git *next; - unsigned long index_size; - unsigned long pack_size; struct pack_window *windows; unsigned int *index_base; + off_t index_size; + off_t pack_size; int pack_local; unsigned char sha1[20]; /* something like ".git/objects/pack/xxxxx.pack" */ -- cgit v1.2.3 From 75025ccdb79b5d6b290c630f8777c9f7bb9d257c Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 23 Dec 2006 02:33:55 -0500 Subject: Create read_or_die utility routine. Like write_or_die read_or_die reads the entire length requested or it kills the current process with a die call. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- cache.h | 1 + 1 file changed, 1 insertion(+) (limited to 'cache.h') diff --git a/cache.h b/cache.h index cc872440ef..e6b716495d 100644 --- a/cache.h +++ b/cache.h @@ -428,6 +428,7 @@ extern char *git_commit_encoding; extern char *git_log_output_encoding; extern int copy_fd(int ifd, int ofd); +extern void read_or_die(int fd, void *buf, size_t count); extern void write_or_die(int fd, const void *buf, size_t count); extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg); -- cgit v1.2.3 From 9bc879c1ced505089e2a1e420d32599bb15b35b5 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 23 Dec 2006 02:34:01 -0500 Subject: Refactor how we open pack files to prepare for multiple windows. To efficiently support mmaping of multiple regions of the same pack file we want to keep the pack's file descriptor open while we are actively working with that pack. So we are now keeping that file descriptor in packed_git.pack_fd and closing it only after we unmap the last window. This is going to increase the number of file descriptors that are in use at once, however that will be bounded by the total number of pack files present and therefore should not be very high. It is a small tradeoff which we may need to revisit after some testing can be done on various repositories and systems. For code clarity we also want to seperate out the implementation of how we open a pack file from the implementation which locates a suitable window (or makes a new one) from the given pack file. Since this is a rather large delta I'm taking advantage of doing it now, in a fairly isolated change. When we open a pack file we need to examine the header and trailer without having a mmap in place, as we may only need to mmap the middle section of this particular pack. Consequently the verification code has been refactored to make use of the new read_or_die function. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- cache.h | 1 + 1 file changed, 1 insertion(+) (limited to 'cache.h') diff --git a/cache.h b/cache.h index e6b716495d..f8a89280fd 100644 --- a/cache.h +++ b/cache.h @@ -351,6 +351,7 @@ extern struct packed_git { unsigned int *index_base; off_t index_size; off_t pack_size; + int pack_fd; int pack_local; unsigned char sha1[20]; /* something like ".git/objects/pack/xxxxx.pack" */ -- cgit v1.2.3 From 03e79c88aa34ce188eb4fb7509e6d127c79c507d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 23 Dec 2006 02:34:08 -0500 Subject: Replace use_packed_git with window cursors. Part of the implementation concept of the sliding mmap window for pack access is to permit multiple windows per pack to be mapped independently. Since the inuse_cnt is associated with the mmap and not with the file, this value is in struct pack_window and needs to be incremented/decremented for each pack_window accessed by any code. To faciliate that implementation we need to replace all uses of use_packed_git() and unuse_packed_git() with a different API that follows struct pack_window objects rather than struct packed_git. The way this works is when we need to start accessing a pack for the first time we should setup a new window 'cursor' by declaring a local and setting it to NULL: struct pack_windows *w_curs = NULL; To obtain the memory region which contains a specific section of the pack file we invoke use_pack(), supplying the address of our current window cursor: unsigned int len; unsigned char *addr = use_pack(p, &w_curs, offset, &len); the returned address `addr` will be the first byte at `offset` within the pack file. The optional variable len will also be updated with the number of bytes remaining following the address. Multiple calls to use_pack() with the same window cursor will update the window cursor, moving it from one window to another when necessary. In this way each window cursor variable maintains only one struct pack_window inuse at a time. Finally before exiting the scope which originally declared the window cursor we must invoke unuse_pack() to unuse the current window (which may be different from the one that was first obtained from use_pack): unuse_pack(&w_curs); This implementation is still not complete with regards to multiple windows, as only one window per pack file is supported right now. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- cache.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'cache.h') diff --git a/cache.h b/cache.h index f8a89280fd..c927f29d31 100644 --- a/cache.h +++ b/cache.h @@ -397,8 +397,8 @@ extern void install_packed_git(struct packed_git *pack); extern struct packed_git *find_sha1_pack(const unsigned char *sha1, struct packed_git *packs); -extern int use_packed_git(struct packed_git *); -extern void unuse_packed_git(struct packed_git *); +extern unsigned char* use_pack(struct packed_git *, struct pack_window **, unsigned long, unsigned int *); +extern void unuse_pack(struct pack_window **); extern struct packed_git *add_packed_git(char *, int, int); extern int num_packed_objects(const struct packed_git *p); extern int nth_packed_object_sha1(const struct packed_git *, int, unsigned char*); -- cgit v1.2.3 From 60bb8b1453e2f93d13e7bb44e8e46c085d2dd752 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 23 Dec 2006 02:34:28 -0500 Subject: Fully activate the sliding window pack access. This finally turns on the sliding window behavior for packfile data access by mapping limited size windows and chaining them under the packed_git->windows list. We consider a given byte offset to be within the window only if there would be at least 20 bytes (one hash worth of data) accessible after the requested offset. This range selection relates to the contract that use_pack() makes with its callers, allowing them to access one hash or one object header without needing to call use_pack() for every byte of data obtained. In the worst case scenario we will map the same page of data twice into memory: once at the end of one window and once again at the start of the next window. This duplicate page mapping will happen only when an object header or a delta base reference is spanned over the end of a window and is always limited to just one page of duplication, as no sane operating system will ever have a page size smaller than a hash. I am assuming that the possible wasted page of virtual address space is going to perform faster than the alternatives, which would be to copy the object header or ref delta into a temporary buffer prior to parsing, or to check the window range on every byte during header parsing. We may decide to revisit this decision in the future since this is just a gut instinct decision and has not actually been proven out by experimental testing. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- cache.h | 1 + 1 file changed, 1 insertion(+) (limited to 'cache.h') diff --git a/cache.h b/cache.h index c927f29d31..c7d7e4dcc0 100644 --- a/cache.h +++ b/cache.h @@ -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 -- cgit v1.2.3 From a53128b60162d7a84adca4206540df5b8e3d9dc8 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 23 Dec 2006 02:34:47 -0500 Subject: Create pack_report() as a debugging aid. Much like the alloc_report() function can be useful to report on object allocation statistics while debugging the new pack_report() function can be useful to report on the behavior of the mmap window code used for packfile access. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- cache.h | 1 + 1 file changed, 1 insertion(+) (limited to 'cache.h') diff --git a/cache.h b/cache.h index c7d7e4dcc0..a5fc23235e 100644 --- a/cache.h +++ b/cache.h @@ -398,6 +398,7 @@ extern void install_packed_git(struct packed_git *pack); extern struct packed_git *find_sha1_pack(const unsigned char *sha1, struct packed_git *packs); +extern void pack_report(); extern unsigned char* use_pack(struct packed_git *, struct pack_window **, unsigned long, unsigned int *); extern void unuse_pack(struct pack_window **); extern struct packed_git *add_packed_git(char *, int, int); -- cgit v1.2.3