2011-04-07 18:23:40 +00:00
|
|
|
#include "builtin.h"
|
2017-06-14 18:07:36 +00:00
|
|
|
#include "config.h"
|
2005-10-12 19:01:31 +00:00
|
|
|
#include "delta.h"
|
2023-03-21 06:26:03 +00:00
|
|
|
#include "environment.h"
|
2023-03-21 06:25:54 +00:00
|
|
|
#include "gettext.h"
|
2023-02-24 00:09:27 +00:00
|
|
|
#include "hex.h"
|
2005-10-12 19:01:31 +00:00
|
|
|
#include "pack.h"
|
|
|
|
#include "csum-file.h"
|
2006-04-02 12:44:09 +00:00
|
|
|
#include "blob.h"
|
|
|
|
#include "commit.h"
|
|
|
|
#include "tree.h"
|
2007-04-18 18:27:45 +00:00
|
|
|
#include "progress.h"
|
2008-02-25 21:46:12 +00:00
|
|
|
#include "fsck.h"
|
2023-05-16 06:34:06 +00:00
|
|
|
#include "strbuf.h"
|
2012-05-24 13:55:44 +00:00
|
|
|
#include "streaming.h"
|
2012-05-06 12:31:55 +00:00
|
|
|
#include "thread-utils.h"
|
2017-08-18 22:20:16 +00:00
|
|
|
#include "packfile.h"
|
2023-04-11 03:00:41 +00:00
|
|
|
#include "pack-revindex.h"
|
2023-04-11 07:41:53 +00:00
|
|
|
#include "object-file.h"
|
2023-05-16 06:34:06 +00:00
|
|
|
#include "object-store-ll.h"
|
2023-04-11 03:00:42 +00:00
|
|
|
#include "oid-array.h"
|
2023-02-24 00:09:33 +00:00
|
|
|
#include "replace-object.h"
|
2019-06-25 13:40:31 +00:00
|
|
|
#include "promisor-remote.h"
|
2023-03-21 06:26:05 +00:00
|
|
|
#include "setup.h"
|
2005-10-12 19:01:31 +00:00
|
|
|
|
|
|
|
static const char index_pack_usage[] =
|
2024-02-01 01:38:02 +00:00
|
|
|
"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--[no-]rev-index] [--verify] [--strict[=<msg-id>=<severity>...]] [--fsck-objects[=<msg-id>=<severity>...]] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
|
2005-10-12 19:01:31 +00:00
|
|
|
|
2011-03-16 07:08:34 +00:00
|
|
|
struct object_entry {
|
2007-06-01 19:18:05 +00:00
|
|
|
struct pack_idx_entry idx;
|
2006-10-20 18:45:21 +00:00
|
|
|
unsigned long size;
|
2015-02-26 10:52:07 +00:00
|
|
|
unsigned char hdr_size;
|
|
|
|
signed char type;
|
|
|
|
signed char real_type;
|
2005-10-12 19:01:31 +00:00
|
|
|
};
|
|
|
|
|
2015-02-26 10:52:07 +00:00
|
|
|
struct object_stat {
|
2011-06-03 22:32:15 +00:00
|
|
|
unsigned delta_depth;
|
|
|
|
int base_object_no;
|
2006-09-21 04:08:33 +00:00
|
|
|
};
|
|
|
|
|
2008-07-14 02:07:44 +00:00
|
|
|
struct base_data {
|
2020-08-24 19:16:36 +00:00
|
|
|
/* Initialized by make_base(). */
|
2008-07-14 02:07:45 +00:00
|
|
|
struct base_data *base;
|
2008-07-14 02:07:46 +00:00
|
|
|
struct object_entry *obj;
|
2012-01-14 12:19:54 +00:00
|
|
|
int ref_first, ref_last;
|
|
|
|
int ofs_first, ofs_last;
|
index-pack: make quantum of work smaller
Currently, when index-pack resolves deltas, it does not split up delta
trees into threads: each delta base root (an object that is not a
REF_DELTA or OFS_DELTA) can go into its own thread, but all deltas on
that root (direct or indirect) are processed in the same thread.
This is a problem when a repository contains a large text file (thus,
delta-able) that is modified many times - delta resolution time during
fetching is dominated by processing the deltas corresponding to that
text file.
This patch contains a solution to that. When cloning using
git -c core.deltabasecachelimit=1g clone \
https://fuchsia.googlesource.com/third_party/vulkan-cts
on my laptop, clone time improved from 3m2s to 2m5s (using 3 threads,
which is the default).
The solution is to have a global work stack. This stack contains delta
bases (objects, whether appearing directly in the packfile or generated
by delta resolution, that themselves have delta children) that need to
be processed; whenever a thread needs work, it peeks at the top of the
stack and processes its next unprocessed child. If a thread finds the
stack empty, it will look for more delta base roots to push on the stack
instead.
The main weakness of having a global work stack is that more time is
spent in the mutex, but profiling has shown that most time is spent in
the resolution of the deltas themselves, so this shouldn't be an issue
in practice. In any case, experimentation (as described in the clone
command above) shows that this patch is a net improvement.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-09-08 19:48:35 +00:00
|
|
|
/*
|
|
|
|
* Threads should increment retain_data if they are about to call
|
|
|
|
* patch_delta() using this struct's data as a base, and decrement this
|
|
|
|
* when they are done. While retain_data is nonzero, this struct's data
|
|
|
|
* will not be freed even if the delta base cache limit is exceeded.
|
|
|
|
*/
|
|
|
|
int retain_data;
|
|
|
|
/*
|
|
|
|
* The number of direct children that have not been fully processed
|
|
|
|
* (entered work_head, entered done_head, left done_head). When this
|
|
|
|
* number reaches zero, this struct base_data can be freed.
|
|
|
|
*/
|
|
|
|
int children_remaining;
|
2020-08-24 19:16:36 +00:00
|
|
|
|
|
|
|
/* Not initialized by make_base(). */
|
index-pack: make quantum of work smaller
Currently, when index-pack resolves deltas, it does not split up delta
trees into threads: each delta base root (an object that is not a
REF_DELTA or OFS_DELTA) can go into its own thread, but all deltas on
that root (direct or indirect) are processed in the same thread.
This is a problem when a repository contains a large text file (thus,
delta-able) that is modified many times - delta resolution time during
fetching is dominated by processing the deltas corresponding to that
text file.
This patch contains a solution to that. When cloning using
git -c core.deltabasecachelimit=1g clone \
https://fuchsia.googlesource.com/third_party/vulkan-cts
on my laptop, clone time improved from 3m2s to 2m5s (using 3 threads,
which is the default).
The solution is to have a global work stack. This stack contains delta
bases (objects, whether appearing directly in the packfile or generated
by delta resolution, that themselves have delta children) that need to
be processed; whenever a thread needs work, it peeks at the top of the
stack and processes its next unprocessed child. If a thread finds the
stack empty, it will look for more delta base roots to push on the stack
instead.
The main weakness of having a global work stack is that more time is
spent in the mutex, but profiling has shown that most time is spent in
the resolution of the deltas themselves, so this shouldn't be an issue
in practice. In any case, experimentation (as described in the clone
command above) shows that this patch is a net improvement.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-09-08 19:48:35 +00:00
|
|
|
struct list_head list;
|
2020-08-24 19:16:36 +00:00
|
|
|
void *data;
|
|
|
|
unsigned long size;
|
2008-07-14 02:07:44 +00:00
|
|
|
};
|
|
|
|
|
index-pack: make quantum of work smaller
Currently, when index-pack resolves deltas, it does not split up delta
trees into threads: each delta base root (an object that is not a
REF_DELTA or OFS_DELTA) can go into its own thread, but all deltas on
that root (direct or indirect) are processed in the same thread.
This is a problem when a repository contains a large text file (thus,
delta-able) that is modified many times - delta resolution time during
fetching is dominated by processing the deltas corresponding to that
text file.
This patch contains a solution to that. When cloning using
git -c core.deltabasecachelimit=1g clone \
https://fuchsia.googlesource.com/third_party/vulkan-cts
on my laptop, clone time improved from 3m2s to 2m5s (using 3 threads,
which is the default).
The solution is to have a global work stack. This stack contains delta
bases (objects, whether appearing directly in the packfile or generated
by delta resolution, that themselves have delta children) that need to
be processed; whenever a thread needs work, it peeks at the top of the
stack and processes its next unprocessed child. If a thread finds the
stack empty, it will look for more delta base roots to push on the stack
instead.
The main weakness of having a global work stack is that more time is
spent in the mutex, but profiling has shown that most time is spent in
the resolution of the deltas themselves, so this shouldn't be an issue
in practice. In any case, experimentation (as described in the clone
command above) shows that this patch is a net improvement.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-09-08 19:48:35 +00:00
|
|
|
/*
|
|
|
|
* Stack of struct base_data that have unprocessed children.
|
|
|
|
* threaded_second_pass() uses this as a source of work (the other being the
|
|
|
|
* objects array).
|
|
|
|
*
|
|
|
|
* Guarded by work_mutex.
|
|
|
|
*/
|
|
|
|
static LIST_HEAD(work_head);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Stack of struct base_data that have children, all of whom have been
|
|
|
|
* processed or are being processed, and at least one child is being processed.
|
|
|
|
* These struct base_data must be kept around until the last child is
|
|
|
|
* processed.
|
|
|
|
*
|
|
|
|
* Guarded by work_mutex.
|
|
|
|
*/
|
|
|
|
static LIST_HEAD(done_head);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* All threads share one delta base cache.
|
|
|
|
*
|
|
|
|
* base_cache_used is guarded by work_mutex, and base_cache_limit is read-only
|
|
|
|
* in a thread.
|
|
|
|
*/
|
|
|
|
static size_t base_cache_used;
|
|
|
|
static size_t base_cache_limit;
|
|
|
|
|
2012-05-06 12:31:55 +00:00
|
|
|
struct thread_local {
|
|
|
|
pthread_t thread;
|
2014-03-25 13:41:41 +00:00
|
|
|
int pack_fd;
|
2012-05-06 12:31:55 +00:00
|
|
|
};
|
|
|
|
|
2018-03-06 10:16:14 +00:00
|
|
|
/* Remember to update object flag allocation in object.h */
|
2008-02-25 21:46:12 +00:00
|
|
|
#define FLAG_LINK (1u<<20)
|
|
|
|
#define FLAG_CHECKED (1u<<21)
|
|
|
|
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
struct ofs_delta_entry {
|
|
|
|
off_t offset;
|
|
|
|
int obj_no;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ref_delta_entry {
|
2018-03-12 02:27:37 +00:00
|
|
|
struct object_id oid;
|
2006-10-26 03:28:17 +00:00
|
|
|
int obj_no;
|
2005-10-12 19:01:31 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct object_entry *objects;
|
2015-02-26 10:52:07 +00:00
|
|
|
static struct object_stat *obj_stat;
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
static struct ofs_delta_entry *ofs_deltas;
|
|
|
|
static struct ref_delta_entry *ref_deltas;
|
2012-05-06 12:31:55 +00:00
|
|
|
static struct thread_local nothread_data;
|
2005-10-12 19:01:31 +00:00
|
|
|
static int nr_objects;
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
static int nr_ofs_deltas;
|
|
|
|
static int nr_ref_deltas;
|
|
|
|
static int ref_deltas_alloc;
|
2006-10-26 03:28:17 +00:00
|
|
|
static int nr_resolved_deltas;
|
2012-05-06 12:31:55 +00:00
|
|
|
static int nr_threads;
|
2005-10-12 19:01:31 +00:00
|
|
|
|
2006-10-23 18:50:18 +00:00
|
|
|
static int from_stdin;
|
2008-02-25 21:46:12 +00:00
|
|
|
static int strict;
|
2013-05-26 01:16:17 +00:00
|
|
|
static int do_fsck_object;
|
2021-03-28 13:15:51 +00:00
|
|
|
static struct fsck_options fsck_options = FSCK_OPTIONS_MISSING_GITMODULES;
|
2006-10-26 03:32:59 +00:00
|
|
|
static int verbose;
|
index-pack: add --progress-title option
Add a --progress-title option to index-pack, when data is piped into
index-pack its progress is a proxy for whatever's feeding it data.
This option will allow us to set a more relevant progress bar title in
"git bundle unbundle", and is also used in my "bundle-uri" RFC
patches[1] by a new caller in fetch-pack.c.
The code change in cmd_index_pack() won't handle
"--progress-title=xyz", only "--progress-title xyz", and the "(i+1)"
style (as opposed to "i + 1") is a bit odd.
Not using the "--long-option=value" style is inconsistent with
existing long options handled by cmd_index_pack(), but makes the code
that needs to call it better (two strvec_push(), instead of needing a
strvec_pushf()). Since the option is internal-only the inconsistency
shouldn't matter.
I'm copying the pattern to handle it as-is from the handling of the
existing "-o" option in the same function, see 9cf6d3357aa (Add
git-index-pack utility, 2005-10-12) for its addition. That's a short
option, but the code to implement the two is the same in functionality
and style. Eventually we'd like to migrate all of this this to
parse_options(), which would make these differences in behavior go
away.
1. https://lore.kernel.org/git/RFC-cover-00.13-0000000000-20210805T150534Z-avarab@gmail.com/
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-09-05 07:34:44 +00:00
|
|
|
static const char *progress_title;
|
index-pack: add flag for showing delta-resolution progress
The index-pack command has two progress meters: one for
"receiving objects", and one for "resolving deltas". You get
neither by default, or both with "-v".
But for a push through receive-pack, we would want only the
"resolving deltas" phase, _not_ the "receiving objects"
progress. There are two reasons for this.
One is simply that existing clients are already printing
"writing objects" progress at the same time. Arguably
"receiving" from the far end is more useful, because it
tells you what has actually gotten there, as opposed to what
might be stuck in a buffer somewhere between the client and
server. But that would require a protocol extension to tell
clients not to print their progress. Possible, but
complexity for little gain.
The second reason is much more important. In a full-duplex
connection like git-over-ssh, we can print progress while
the pack is incoming, and it will immediately get to the
client. But for a half-duplex connection like git-over-http,
we should not say anything until we have received the full
request. Anything we write is subject to being stuck in a
buffer by the webserver. Worse, we can end up in a deadlock
if that buffer fills up.
So our best bet is to avoid writing anything that isn't a
small fixed size until we've received the full pack.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-07-15 10:34:22 +00:00
|
|
|
static int show_resolving_progress;
|
2013-03-19 13:01:15 +00:00
|
|
|
static int show_stat;
|
2013-05-26 01:16:17 +00:00
|
|
|
static int check_self_contained_and_connected;
|
2006-10-26 03:32:59 +00:00
|
|
|
|
2007-10-30 18:57:32 +00:00
|
|
|
static struct progress *progress;
|
2006-10-23 18:50:18 +00:00
|
|
|
|
2006-10-20 18:45:21 +00:00
|
|
|
/* We always read in 4kB chunks. */
|
|
|
|
static unsigned char input_buffer[4096];
|
2007-04-09 05:06:30 +00:00
|
|
|
static unsigned int input_offset, input_len;
|
|
|
|
static off_t consumed_bytes;
|
2016-08-24 18:41:55 +00:00
|
|
|
static off_t max_input_size;
|
2011-06-03 22:32:16 +00:00
|
|
|
static unsigned deepest_delta;
|
2018-02-01 02:18:39 +00:00
|
|
|
static git_hash_ctx input_ctx;
|
2007-04-09 05:06:32 +00:00
|
|
|
static uint32_t input_crc32;
|
2014-03-25 13:41:41 +00:00
|
|
|
static int input_fd, output_fd;
|
|
|
|
static const char *curr_pack;
|
2006-10-20 18:45:21 +00:00
|
|
|
|
2012-05-06 12:31:55 +00:00
|
|
|
static struct thread_local *thread_data;
|
|
|
|
static int nr_dispatched;
|
|
|
|
static int threads_active;
|
|
|
|
|
|
|
|
static pthread_mutex_t read_mutex;
|
|
|
|
#define read_lock() lock_mutex(&read_mutex)
|
|
|
|
#define read_unlock() unlock_mutex(&read_mutex)
|
|
|
|
|
|
|
|
static pthread_mutex_t counter_mutex;
|
|
|
|
#define counter_lock() lock_mutex(&counter_mutex)
|
|
|
|
#define counter_unlock() unlock_mutex(&counter_mutex)
|
|
|
|
|
|
|
|
static pthread_mutex_t work_mutex;
|
|
|
|
#define work_lock() lock_mutex(&work_mutex)
|
|
|
|
#define work_unlock() unlock_mutex(&work_mutex)
|
|
|
|
|
2013-03-19 13:01:15 +00:00
|
|
|
static pthread_mutex_t deepest_delta_mutex;
|
|
|
|
#define deepest_delta_lock() lock_mutex(&deepest_delta_mutex)
|
|
|
|
#define deepest_delta_unlock() unlock_mutex(&deepest_delta_mutex)
|
|
|
|
|
2012-05-06 12:31:55 +00:00
|
|
|
static pthread_key_t key;
|
|
|
|
|
|
|
|
static inline void lock_mutex(pthread_mutex_t *mutex)
|
|
|
|
{
|
|
|
|
if (threads_active)
|
|
|
|
pthread_mutex_lock(mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void unlock_mutex(pthread_mutex_t *mutex)
|
|
|
|
{
|
|
|
|
if (threads_active)
|
|
|
|
pthread_mutex_unlock(mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Mutex and conditional variable can't be statically-initialized on Windows.
|
|
|
|
*/
|
|
|
|
static void init_thread(void)
|
|
|
|
{
|
2014-03-25 13:41:41 +00:00
|
|
|
int i;
|
2012-05-06 12:31:55 +00:00
|
|
|
init_recursive_mutex(&read_mutex);
|
|
|
|
pthread_mutex_init(&counter_mutex, NULL);
|
|
|
|
pthread_mutex_init(&work_mutex, NULL);
|
2013-03-19 13:01:15 +00:00
|
|
|
if (show_stat)
|
|
|
|
pthread_mutex_init(&deepest_delta_mutex, NULL);
|
2012-05-06 12:31:55 +00:00
|
|
|
pthread_key_create(&key, NULL);
|
2021-03-13 16:17:22 +00:00
|
|
|
CALLOC_ARRAY(thread_data, nr_threads);
|
2014-03-25 13:41:41 +00:00
|
|
|
for (i = 0; i < nr_threads; i++) {
|
2021-09-10 20:25:50 +00:00
|
|
|
thread_data[i].pack_fd = xopen(curr_pack, O_RDONLY);
|
2014-03-25 13:41:41 +00:00
|
|
|
}
|
|
|
|
|
2012-05-06 12:31:55 +00:00
|
|
|
threads_active = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void cleanup_thread(void)
|
|
|
|
{
|
2014-03-25 13:41:41 +00:00
|
|
|
int i;
|
2012-05-06 12:31:55 +00:00
|
|
|
if (!threads_active)
|
|
|
|
return;
|
|
|
|
threads_active = 0;
|
|
|
|
pthread_mutex_destroy(&read_mutex);
|
|
|
|
pthread_mutex_destroy(&counter_mutex);
|
|
|
|
pthread_mutex_destroy(&work_mutex);
|
2013-03-19 13:01:15 +00:00
|
|
|
if (show_stat)
|
|
|
|
pthread_mutex_destroy(&deepest_delta_mutex);
|
2014-03-25 13:41:41 +00:00
|
|
|
for (i = 0; i < nr_threads; i++)
|
|
|
|
close(thread_data[i].pack_fd);
|
2012-05-06 12:31:55 +00:00
|
|
|
pthread_key_delete(key);
|
|
|
|
free(thread_data);
|
|
|
|
}
|
|
|
|
|
2021-03-28 13:15:35 +00:00
|
|
|
static int mark_link(struct object *obj, enum object_type type,
|
2023-07-03 06:44:18 +00:00
|
|
|
void *data UNUSED,
|
|
|
|
struct fsck_options *options UNUSED)
|
2008-02-25 21:46:12 +00:00
|
|
|
{
|
|
|
|
if (!obj)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (type != OBJ_ANY && obj->type != type)
|
2015-11-10 02:22:28 +00:00
|
|
|
die(_("object type mismatch at %s"), oid_to_hex(&obj->oid));
|
2008-02-25 21:46:12 +00:00
|
|
|
|
|
|
|
obj->flags |= FLAG_LINK;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The content of each linked object must have been checked
|
|
|
|
or it must be already present in the object database */
|
2013-05-26 01:16:17 +00:00
|
|
|
static unsigned check_object(struct object *obj)
|
2008-02-25 21:46:12 +00:00
|
|
|
{
|
|
|
|
if (!obj)
|
2013-05-26 01:16:17 +00:00
|
|
|
return 0;
|
2008-02-25 21:46:12 +00:00
|
|
|
|
|
|
|
if (!(obj->flags & FLAG_LINK))
|
2013-05-26 01:16:17 +00:00
|
|
|
return 0;
|
2008-02-25 21:46:12 +00:00
|
|
|
|
|
|
|
if (!(obj->flags & FLAG_CHECKED)) {
|
|
|
|
unsigned long size;
|
2018-04-25 18:20:59 +00:00
|
|
|
int type = oid_object_info(the_repository, &obj->oid, &size);
|
2014-05-12 04:38:39 +00:00
|
|
|
if (type <= 0)
|
|
|
|
die(_("did not receive expected object %s"),
|
2015-11-10 02:22:28 +00:00
|
|
|
oid_to_hex(&obj->oid));
|
2014-05-12 04:38:39 +00:00
|
|
|
if (type != obj->type)
|
|
|
|
die(_("object %s: expected type %s, found %s"),
|
2015-11-10 02:22:28 +00:00
|
|
|
oid_to_hex(&obj->oid),
|
2018-02-14 18:59:24 +00:00
|
|
|
type_name(obj->type), type_name(type));
|
2008-02-25 21:46:12 +00:00
|
|
|
obj->flags |= FLAG_CHECKED;
|
2013-05-26 01:16:17 +00:00
|
|
|
return 1;
|
2008-02-25 21:46:12 +00:00
|
|
|
}
|
2013-05-26 01:16:17 +00:00
|
|
|
|
|
|
|
return 0;
|
2008-02-25 21:46:12 +00:00
|
|
|
}
|
|
|
|
|
2013-05-26 01:16:17 +00:00
|
|
|
static unsigned check_objects(void)
|
2008-02-25 21:46:12 +00:00
|
|
|
{
|
2013-05-26 01:16:17 +00:00
|
|
|
unsigned i, max, foreign_nr = 0;
|
2008-02-25 21:46:12 +00:00
|
|
|
|
|
|
|
max = get_max_object_index();
|
2019-03-31 23:12:35 +00:00
|
|
|
|
|
|
|
if (verbose)
|
|
|
|
progress = start_delayed_progress(_("Checking objects"), max);
|
|
|
|
|
|
|
|
for (i = 0; i < max; i++) {
|
2013-05-26 01:16:17 +00:00
|
|
|
foreign_nr += check_object(get_indexed_object(i));
|
2019-03-31 23:12:35 +00:00
|
|
|
display_progress(progress, i + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
stop_progress(&progress);
|
2013-05-26 01:16:17 +00:00
|
|
|
return foreign_nr;
|
2008-02-25 21:46:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-10-26 03:28:17 +00:00
|
|
|
/* Discard current buffer used content. */
|
2006-11-18 12:07:06 +00:00
|
|
|
static void flush(void)
|
2006-10-26 03:28:17 +00:00
|
|
|
{
|
|
|
|
if (input_offset) {
|
|
|
|
if (output_fd >= 0)
|
|
|
|
write_or_die(output_fd, input_buffer, input_offset);
|
2018-02-01 02:18:39 +00:00
|
|
|
the_hash_algo->update_fn(&input_ctx, input_buffer, input_offset);
|
2006-12-11 18:06:34 +00:00
|
|
|
memmove(input_buffer, input_buffer + input_offset, input_len);
|
2006-10-26 03:28:17 +00:00
|
|
|
input_offset = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-10-20 18:45:21 +00:00
|
|
|
/*
|
|
|
|
* Make sure at least "min" bytes are available in the buffer, and
|
|
|
|
* return the pointer to the buffer.
|
|
|
|
*/
|
2006-10-27 20:14:23 +00:00
|
|
|
static void *fill(int min)
|
2005-10-12 19:01:31 +00:00
|
|
|
{
|
2006-10-20 18:45:21 +00:00
|
|
|
if (min <= input_len)
|
|
|
|
return input_buffer + input_offset;
|
|
|
|
if (min > sizeof(input_buffer))
|
2012-04-23 12:30:29 +00:00
|
|
|
die(Q_("cannot fill %d byte",
|
|
|
|
"cannot fill %d bytes",
|
|
|
|
min),
|
|
|
|
min);
|
2006-10-26 03:28:17 +00:00
|
|
|
flush();
|
2006-10-20 18:45:21 +00:00
|
|
|
do {
|
2007-05-15 12:49:22 +00:00
|
|
|
ssize_t ret = xread(input_fd, input_buffer + input_len,
|
2006-10-20 18:45:21 +00:00
|
|
|
sizeof(input_buffer) - input_len);
|
|
|
|
if (ret <= 0) {
|
|
|
|
if (!ret)
|
2012-04-23 12:30:29 +00:00
|
|
|
die(_("early EOF"));
|
|
|
|
die_errno(_("read error on input"));
|
2006-10-20 18:45:21 +00:00
|
|
|
}
|
|
|
|
input_len += ret;
|
2007-11-05 03:15:41 +00:00
|
|
|
if (from_stdin)
|
|
|
|
display_throughput(progress, consumed_bytes + input_len);
|
2006-10-20 18:45:21 +00:00
|
|
|
} while (input_len < min);
|
|
|
|
return input_buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void use(int bytes)
|
|
|
|
{
|
|
|
|
if (bytes > input_len)
|
2012-04-23 12:30:29 +00:00
|
|
|
die(_("used more bytes than were available"));
|
2007-04-09 05:06:32 +00:00
|
|
|
input_crc32 = crc32(input_crc32, input_buffer + input_offset, bytes);
|
2006-10-20 18:45:21 +00:00
|
|
|
input_len -= bytes;
|
|
|
|
input_offset += bytes;
|
2007-04-09 05:06:30 +00:00
|
|
|
|
|
|
|
/* make sure off_t is sufficiently large not to wrap */
|
2010-10-05 07:24:10 +00:00
|
|
|
if (signed_add_overflows(consumed_bytes, bytes))
|
2012-04-23 12:30:29 +00:00
|
|
|
die(_("pack too large for current definition of off_t"));
|
2006-10-20 18:45:21 +00:00
|
|
|
consumed_bytes += bytes;
|
2022-02-24 00:07:20 +00:00
|
|
|
if (max_input_size && consumed_bytes > max_input_size) {
|
|
|
|
struct strbuf size_limit = STRBUF_INIT;
|
|
|
|
strbuf_humanise_bytes(&size_limit, max_input_size);
|
|
|
|
die(_("pack exceeds maximum allowed size (%s)"),
|
|
|
|
size_limit.buf);
|
|
|
|
}
|
2006-10-20 18:45:21 +00:00
|
|
|
}
|
2005-10-12 19:01:31 +00:00
|
|
|
|
2010-01-22 15:55:19 +00:00
|
|
|
static const char *open_pack_file(const char *pack_name)
|
2006-10-20 18:45:21 +00:00
|
|
|
{
|
2006-10-23 18:50:18 +00:00
|
|
|
if (from_stdin) {
|
|
|
|
input_fd = 0;
|
|
|
|
if (!pack_name) {
|
2017-03-28 19:45:43 +00:00
|
|
|
struct strbuf tmp_file = STRBUF_INIT;
|
|
|
|
output_fd = odb_mkstemp(&tmp_file,
|
2009-02-25 07:11:29 +00:00
|
|
|
"pack/tmp_pack_XXXXXX");
|
2017-03-28 19:45:43 +00:00
|
|
|
pack_name = strbuf_detach(&tmp_file, NULL);
|
2017-03-28 19:45:25 +00:00
|
|
|
} else {
|
2021-08-25 20:16:46 +00:00
|
|
|
output_fd = xopen(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600);
|
2017-03-28 19:45:25 +00:00
|
|
|
}
|
2014-03-25 13:41:41 +00:00
|
|
|
nothread_data.pack_fd = output_fd;
|
2006-10-23 18:50:18 +00:00
|
|
|
} else {
|
2021-08-25 20:16:46 +00:00
|
|
|
input_fd = xopen(pack_name, O_RDONLY);
|
2006-10-23 18:50:18 +00:00
|
|
|
output_fd = -1;
|
2014-03-25 13:41:41 +00:00
|
|
|
nothread_data.pack_fd = input_fd;
|
2006-10-23 18:50:18 +00:00
|
|
|
}
|
2018-02-01 02:18:39 +00:00
|
|
|
the_hash_algo->init_fn(&input_ctx);
|
2006-10-23 18:50:18 +00:00
|
|
|
return pack_name;
|
2005-10-12 19:01:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void parse_pack_header(void)
|
|
|
|
{
|
2006-10-20 18:45:21 +00:00
|
|
|
struct pack_header *hdr = fill(sizeof(struct pack_header));
|
2005-10-12 19:01:31 +00:00
|
|
|
|
|
|
|
/* Header consistency check */
|
|
|
|
if (hdr->hdr_signature != htonl(PACK_SIGNATURE))
|
2012-04-23 12:30:29 +00:00
|
|
|
die(_("pack signature mismatch"));
|
2006-02-09 22:50:04 +00:00
|
|
|
if (!pack_version_ok(hdr->hdr_version))
|
2012-08-31 12:13:04 +00:00
|
|
|
die(_("pack version %"PRIu32" unsupported"),
|
2008-07-03 15:52:09 +00:00
|
|
|
ntohl(hdr->hdr_version));
|
2005-10-12 19:01:31 +00:00
|
|
|
|
|
|
|
nr_objects = ntohl(hdr->hdr_entries);
|
2006-10-20 18:45:21 +00:00
|
|
|
use(sizeof(struct pack_header));
|
2005-10-12 19:01:31 +00:00
|
|
|
}
|
|
|
|
|
2021-07-10 08:47:27 +00:00
|
|
|
__attribute__((format (printf, 2, 3)))
|
2016-07-13 15:44:01 +00:00
|
|
|
static NORETURN void bad_object(off_t offset, const char *format, ...)
|
2005-10-12 19:01:31 +00:00
|
|
|
{
|
|
|
|
va_list params;
|
|
|
|
char buf[1024];
|
|
|
|
|
|
|
|
va_start(params, format);
|
|
|
|
vsnprintf(buf, sizeof(buf), format, params);
|
|
|
|
va_end(params);
|
2016-07-13 15:44:01 +00:00
|
|
|
die(_("pack has bad object at offset %"PRIuMAX": %s"),
|
|
|
|
(uintmax_t)offset, buf);
|
2005-10-12 19:01:31 +00:00
|
|
|
}
|
|
|
|
|
2012-05-06 12:31:55 +00:00
|
|
|
static inline struct thread_local *get_thread_data(void)
|
|
|
|
{
|
2018-11-03 08:48:40 +00:00
|
|
|
if (HAVE_THREADS) {
|
|
|
|
if (threads_active)
|
|
|
|
return pthread_getspecific(key);
|
|
|
|
assert(!threads_active &&
|
|
|
|
"This should only be reached when all threads are gone");
|
|
|
|
}
|
2012-05-06 12:31:55 +00:00
|
|
|
return ¬hread_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void set_thread_data(struct thread_local *data)
|
|
|
|
{
|
|
|
|
if (threads_active)
|
|
|
|
pthread_setspecific(key, data);
|
|
|
|
}
|
|
|
|
|
2008-10-17 19:57:58 +00:00
|
|
|
static void free_base_data(struct base_data *c)
|
|
|
|
{
|
|
|
|
if (c->data) {
|
2017-06-15 23:15:46 +00:00
|
|
|
FREE_AND_NULL(c->data);
|
index-pack: make quantum of work smaller
Currently, when index-pack resolves deltas, it does not split up delta
trees into threads: each delta base root (an object that is not a
REF_DELTA or OFS_DELTA) can go into its own thread, but all deltas on
that root (direct or indirect) are processed in the same thread.
This is a problem when a repository contains a large text file (thus,
delta-able) that is modified many times - delta resolution time during
fetching is dominated by processing the deltas corresponding to that
text file.
This patch contains a solution to that. When cloning using
git -c core.deltabasecachelimit=1g clone \
https://fuchsia.googlesource.com/third_party/vulkan-cts
on my laptop, clone time improved from 3m2s to 2m5s (using 3 threads,
which is the default).
The solution is to have a global work stack. This stack contains delta
bases (objects, whether appearing directly in the packfile or generated
by delta resolution, that themselves have delta children) that need to
be processed; whenever a thread needs work, it peeks at the top of the
stack and processes its next unprocessed child. If a thread finds the
stack empty, it will look for more delta base roots to push on the stack
instead.
The main weakness of having a global work stack is that more time is
spent in the mutex, but profiling has shown that most time is spent in
the resolution of the deltas themselves, so this shouldn't be an issue
in practice. In any case, experimentation (as described in the clone
command above) shows that this patch is a net improvement.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-09-08 19:48:35 +00:00
|
|
|
base_cache_used -= c->size;
|
2008-10-17 19:57:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-15 04:45:34 +00:00
|
|
|
static void prune_base_data(struct base_data *retain)
|
|
|
|
{
|
index-pack: make quantum of work smaller
Currently, when index-pack resolves deltas, it does not split up delta
trees into threads: each delta base root (an object that is not a
REF_DELTA or OFS_DELTA) can go into its own thread, but all deltas on
that root (direct or indirect) are processed in the same thread.
This is a problem when a repository contains a large text file (thus,
delta-able) that is modified many times - delta resolution time during
fetching is dominated by processing the deltas corresponding to that
text file.
This patch contains a solution to that. When cloning using
git -c core.deltabasecachelimit=1g clone \
https://fuchsia.googlesource.com/third_party/vulkan-cts
on my laptop, clone time improved from 3m2s to 2m5s (using 3 threads,
which is the default).
The solution is to have a global work stack. This stack contains delta
bases (objects, whether appearing directly in the packfile or generated
by delta resolution, that themselves have delta children) that need to
be processed; whenever a thread needs work, it peeks at the top of the
stack and processes its next unprocessed child. If a thread finds the
stack empty, it will look for more delta base roots to push on the stack
instead.
The main weakness of having a global work stack is that more time is
spent in the mutex, but profiling has shown that most time is spent in
the resolution of the deltas themselves, so this shouldn't be an issue
in practice. In any case, experimentation (as described in the clone
command above) shows that this patch is a net improvement.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-09-08 19:48:35 +00:00
|
|
|
struct list_head *pos;
|
2008-07-15 04:45:34 +00:00
|
|
|
|
index-pack: make quantum of work smaller
Currently, when index-pack resolves deltas, it does not split up delta
trees into threads: each delta base root (an object that is not a
REF_DELTA or OFS_DELTA) can go into its own thread, but all deltas on
that root (direct or indirect) are processed in the same thread.
This is a problem when a repository contains a large text file (thus,
delta-able) that is modified many times - delta resolution time during
fetching is dominated by processing the deltas corresponding to that
text file.
This patch contains a solution to that. When cloning using
git -c core.deltabasecachelimit=1g clone \
https://fuchsia.googlesource.com/third_party/vulkan-cts
on my laptop, clone time improved from 3m2s to 2m5s (using 3 threads,
which is the default).
The solution is to have a global work stack. This stack contains delta
bases (objects, whether appearing directly in the packfile or generated
by delta resolution, that themselves have delta children) that need to
be processed; whenever a thread needs work, it peeks at the top of the
stack and processes its next unprocessed child. If a thread finds the
stack empty, it will look for more delta base roots to push on the stack
instead.
The main weakness of having a global work stack is that more time is
spent in the mutex, but profiling has shown that most time is spent in
the resolution of the deltas themselves, so this shouldn't be an issue
in practice. In any case, experimentation (as described in the clone
command above) shows that this patch is a net improvement.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-09-08 19:48:35 +00:00
|
|
|
if (base_cache_used <= base_cache_limit)
|
2020-08-24 19:16:35 +00:00
|
|
|
return;
|
2008-07-14 02:07:45 +00:00
|
|
|
|
index-pack: make quantum of work smaller
Currently, when index-pack resolves deltas, it does not split up delta
trees into threads: each delta base root (an object that is not a
REF_DELTA or OFS_DELTA) can go into its own thread, but all deltas on
that root (direct or indirect) are processed in the same thread.
This is a problem when a repository contains a large text file (thus,
delta-able) that is modified many times - delta resolution time during
fetching is dominated by processing the deltas corresponding to that
text file.
This patch contains a solution to that. When cloning using
git -c core.deltabasecachelimit=1g clone \
https://fuchsia.googlesource.com/third_party/vulkan-cts
on my laptop, clone time improved from 3m2s to 2m5s (using 3 threads,
which is the default).
The solution is to have a global work stack. This stack contains delta
bases (objects, whether appearing directly in the packfile or generated
by delta resolution, that themselves have delta children) that need to
be processed; whenever a thread needs work, it peeks at the top of the
stack and processes its next unprocessed child. If a thread finds the
stack empty, it will look for more delta base roots to push on the stack
instead.
The main weakness of having a global work stack is that more time is
spent in the mutex, but profiling has shown that most time is spent in
the resolution of the deltas themselves, so this shouldn't be an issue
in practice. In any case, experimentation (as described in the clone
command above) shows that this patch is a net improvement.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-09-08 19:48:35 +00:00
|
|
|
list_for_each_prev(pos, &done_head) {
|
|
|
|
struct base_data *b = list_entry(pos, struct base_data, list);
|
|
|
|
if (b->retain_data || b == retain)
|
|
|
|
continue;
|
|
|
|
if (b->data) {
|
|
|
|
free_base_data(b);
|
|
|
|
if (base_cache_used <= base_cache_limit)
|
|
|
|
return;
|
|
|
|
}
|
2008-07-15 04:45:34 +00:00
|
|
|
}
|
2008-07-14 02:07:45 +00:00
|
|
|
|
index-pack: make quantum of work smaller
Currently, when index-pack resolves deltas, it does not split up delta
trees into threads: each delta base root (an object that is not a
REF_DELTA or OFS_DELTA) can go into its own thread, but all deltas on
that root (direct or indirect) are processed in the same thread.
This is a problem when a repository contains a large text file (thus,
delta-able) that is modified many times - delta resolution time during
fetching is dominated by processing the deltas corresponding to that
text file.
This patch contains a solution to that. When cloning using
git -c core.deltabasecachelimit=1g clone \
https://fuchsia.googlesource.com/third_party/vulkan-cts
on my laptop, clone time improved from 3m2s to 2m5s (using 3 threads,
which is the default).
The solution is to have a global work stack. This stack contains delta
bases (objects, whether appearing directly in the packfile or generated
by delta resolution, that themselves have delta children) that need to
be processed; whenever a thread needs work, it peeks at the top of the
stack and processes its next unprocessed child. If a thread finds the
stack empty, it will look for more delta base roots to push on the stack
instead.
The main weakness of having a global work stack is that more time is
spent in the mutex, but profiling has shown that most time is spent in
the resolution of the deltas themselves, so this shouldn't be an issue
in practice. In any case, experimentation (as described in the clone
command above) shows that this patch is a net improvement.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-09-08 19:48:35 +00:00
|
|
|
list_for_each_prev(pos, &work_head) {
|
|
|
|
struct base_data *b = list_entry(pos, struct base_data, list);
|
|
|
|
if (b->retain_data || b == retain)
|
|
|
|
continue;
|
|
|
|
if (b->data) {
|
|
|
|
free_base_data(b);
|
|
|
|
if (base_cache_used <= base_cache_limit)
|
|
|
|
return;
|
|
|
|
}
|
2020-08-24 19:16:35 +00:00
|
|
|
}
|
2008-07-14 02:07:45 +00:00
|
|
|
}
|
|
|
|
|
2012-05-23 14:09:46 +00:00
|
|
|
static int is_delta_type(enum object_type type)
|
|
|
|
{
|
|
|
|
return (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA);
|
|
|
|
}
|
|
|
|
|
2016-07-13 15:44:02 +00:00
|
|
|
static void *unpack_entry_data(off_t offset, unsigned long size,
|
2018-02-01 02:18:39 +00:00
|
|
|
enum object_type type, struct object_id *oid)
|
2005-10-12 19:01:31 +00:00
|
|
|
{
|
2012-05-23 14:09:47 +00:00
|
|
|
static char fixed_buf[8192];
|
2010-04-12 16:12:06 +00:00
|
|
|
int status;
|
2011-06-10 18:52:15 +00:00
|
|
|
git_zstream stream;
|
2012-05-23 14:09:47 +00:00
|
|
|
void *buf;
|
2018-02-01 02:18:39 +00:00
|
|
|
git_hash_ctx c;
|
2012-05-23 14:09:46 +00:00
|
|
|
char hdr[32];
|
|
|
|
int hdrlen;
|
|
|
|
|
|
|
|
if (!is_delta_type(type)) {
|
2022-02-04 23:48:25 +00:00
|
|
|
hdrlen = format_object_header(hdr, sizeof(hdr), type, size);
|
2018-02-01 02:18:39 +00:00
|
|
|
the_hash_algo->init_fn(&c);
|
|
|
|
the_hash_algo->update_fn(&c, hdr, hdrlen);
|
2012-05-23 14:09:46 +00:00
|
|
|
} else
|
2018-02-01 02:18:39 +00:00
|
|
|
oid = NULL;
|
2012-05-23 14:09:47 +00:00
|
|
|
if (type == OBJ_BLOB && size > big_file_threshold)
|
|
|
|
buf = fixed_buf;
|
|
|
|
else
|
2014-12-08 14:17:55 +00:00
|
|
|
buf = xmallocz(size);
|
2005-10-12 19:01:31 +00:00
|
|
|
|
|
|
|
memset(&stream, 0, sizeof(stream));
|
2010-04-12 16:12:06 +00:00
|
|
|
git_inflate_init(&stream);
|
2005-10-12 19:01:31 +00:00
|
|
|
stream.next_out = buf;
|
2012-05-23 14:09:47 +00:00
|
|
|
stream.avail_out = buf == fixed_buf ? sizeof(fixed_buf) : size;
|
2005-10-12 19:01:31 +00:00
|
|
|
|
2010-04-12 16:12:06 +00:00
|
|
|
do {
|
2012-05-23 14:09:46 +00:00
|
|
|
unsigned char *last_out = stream.next_out;
|
2006-10-20 18:45:21 +00:00
|
|
|
stream.next_in = fill(1);
|
|
|
|
stream.avail_in = input_len;
|
2010-04-12 16:12:06 +00:00
|
|
|
status = git_inflate(&stream, 0);
|
|
|
|
use(input_len - stream.avail_in);
|
2018-02-01 02:18:39 +00:00
|
|
|
if (oid)
|
|
|
|
the_hash_algo->update_fn(&c, last_out, stream.next_out - last_out);
|
2012-05-23 14:09:47 +00:00
|
|
|
if (buf == fixed_buf) {
|
|
|
|
stream.next_out = buf;
|
|
|
|
stream.avail_out = sizeof(fixed_buf);
|
|
|
|
}
|
2010-04-12 16:12:06 +00:00
|
|
|
} while (status == Z_OK);
|
|
|
|
if (stream.total_out != size || status != Z_STREAM_END)
|
2012-04-23 12:30:29 +00:00
|
|
|
bad_object(offset, _("inflate returned %d"), status);
|
2009-01-08 03:54:47 +00:00
|
|
|
git_inflate_end(&stream);
|
2018-02-01 02:18:39 +00:00
|
|
|
if (oid)
|
2021-04-26 01:02:53 +00:00
|
|
|
the_hash_algo->final_oid_fn(oid, &c);
|
2012-05-23 14:09:47 +00:00
|
|
|
return buf == fixed_buf ? NULL : buf;
|
2005-10-12 19:01:31 +00:00
|
|
|
}
|
|
|
|
|
2012-05-23 14:09:46 +00:00
|
|
|
static void *unpack_raw_entry(struct object_entry *obj,
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
off_t *ofs_offset,
|
2018-02-01 02:18:39 +00:00
|
|
|
struct object_id *ref_oid,
|
|
|
|
struct object_id *oid)
|
2005-10-12 19:01:31 +00:00
|
|
|
{
|
Fix big left-shifts of unsigned char
Shifting 'unsigned char' or 'unsigned short' left can result in sign
extension errors, since the C integer promotion rules means that the
unsigned char/short will get implicitly promoted to a signed 'int' due to
the shift (or due to other operations).
This normally doesn't matter, but if you shift things up sufficiently, it
will now set the sign bit in 'int', and a subsequent cast to a bigger type
(eg 'long' or 'unsigned long') will now sign-extend the value despite the
original expression being unsigned.
One example of this would be something like
unsigned long size;
unsigned char c;
size += c << 24;
where despite all the variables being unsigned, 'c << 24' ends up being a
signed entity, and will get sign-extended when then doing the addition in
an 'unsigned long' type.
Since git uses 'unsigned char' pointers extensively, we actually have this
bug in a couple of places.
I may have missed some, but this is the result of looking at
git grep '[^0-9 ][ ]*<<[ ][a-z]' -- '*.c' '*.h'
git grep '<<[ ]*24'
which catches at least the common byte cases (shifting variables by a
variable amount, and shifting by 24 bits).
I also grepped for just 'unsigned char' variables in general, and
converted the ones that most obviously ended up getting implicitly cast
immediately anyway (eg hash_name(), encode_85()).
In addition to just avoiding 'unsigned char', this patch also tries to use
a common idiom for the delta header size thing. We had three different
variations on it: "& 0x7fUL" in one place (getting the sign extension
right), and "& ~0x80" and "& 0x7f" in two other places (not getting it
right). Apart from making them all just avoid using "unsigned char" at
all, I also unified them to then use a simple "& 0x7f".
I considered making a sparse extension which warns about doing implicit
casts from unsigned types to signed types, but it gets rather complex very
quickly, so this is just a hack.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-06-18 00:22:27 +00:00
|
|
|
unsigned char *p;
|
|
|
|
unsigned long size, c;
|
2007-04-09 05:06:30 +00:00
|
|
|
off_t base_offset;
|
2005-10-12 19:01:31 +00:00
|
|
|
unsigned shift;
|
2007-04-09 05:06:32 +00:00
|
|
|
void *data;
|
2005-10-12 19:01:31 +00:00
|
|
|
|
2007-06-01 19:18:05 +00:00
|
|
|
obj->idx.offset = consumed_bytes;
|
2011-04-03 07:06:54 +00:00
|
|
|
input_crc32 = crc32(0, NULL, 0);
|
2006-10-20 18:45:21 +00:00
|
|
|
|
|
|
|
p = fill(1);
|
|
|
|
c = *p;
|
|
|
|
use(1);
|
|
|
|
obj->type = (c >> 4) & 7;
|
2005-10-12 19:01:31 +00:00
|
|
|
size = (c & 15);
|
|
|
|
shift = 4;
|
|
|
|
while (c & 0x80) {
|
2006-10-20 18:45:21 +00:00
|
|
|
p = fill(1);
|
|
|
|
c = *p;
|
|
|
|
use(1);
|
Fix big left-shifts of unsigned char
Shifting 'unsigned char' or 'unsigned short' left can result in sign
extension errors, since the C integer promotion rules means that the
unsigned char/short will get implicitly promoted to a signed 'int' due to
the shift (or due to other operations).
This normally doesn't matter, but if you shift things up sufficiently, it
will now set the sign bit in 'int', and a subsequent cast to a bigger type
(eg 'long' or 'unsigned long') will now sign-extend the value despite the
original expression being unsigned.
One example of this would be something like
unsigned long size;
unsigned char c;
size += c << 24;
where despite all the variables being unsigned, 'c << 24' ends up being a
signed entity, and will get sign-extended when then doing the addition in
an 'unsigned long' type.
Since git uses 'unsigned char' pointers extensively, we actually have this
bug in a couple of places.
I may have missed some, but this is the result of looking at
git grep '[^0-9 ][ ]*<<[ ][a-z]' -- '*.c' '*.h'
git grep '<<[ ]*24'
which catches at least the common byte cases (shifting variables by a
variable amount, and shifting by 24 bits).
I also grepped for just 'unsigned char' variables in general, and
converted the ones that most obviously ended up getting implicitly cast
immediately anyway (eg hash_name(), encode_85()).
In addition to just avoiding 'unsigned char', this patch also tries to use
a common idiom for the delta header size thing. We had three different
variations on it: "& 0x7fUL" in one place (getting the sign extension
right), and "& ~0x80" and "& 0x7f" in two other places (not getting it
right). Apart from making them all just avoid using "unsigned char" at
all, I also unified them to then use a simple "& 0x7f".
I considered making a sparse extension which warns about doing implicit
casts from unsigned types to signed types, but it gets rather complex very
quickly, so this is just a hack.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-06-18 00:22:27 +00:00
|
|
|
size += (c & 0x7f) << shift;
|
2005-10-12 19:01:31 +00:00
|
|
|
shift += 7;
|
|
|
|
}
|
2006-10-20 18:45:21 +00:00
|
|
|
obj->size = size;
|
2005-10-12 19:01:31 +00:00
|
|
|
|
2006-10-20 18:45:21 +00:00
|
|
|
switch (obj->type) {
|
2006-09-21 04:06:49 +00:00
|
|
|
case OBJ_REF_DELTA:
|
2021-04-26 01:02:50 +00:00
|
|
|
oidread(ref_oid, fill(the_hash_algo->rawsz));
|
2018-02-01 02:18:39 +00:00
|
|
|
use(the_hash_algo->rawsz);
|
2006-09-21 04:08:33 +00:00
|
|
|
break;
|
|
|
|
case OBJ_OFS_DELTA:
|
2006-10-20 18:45:21 +00:00
|
|
|
p = fill(1);
|
|
|
|
c = *p;
|
|
|
|
use(1);
|
2006-09-21 04:08:33 +00:00
|
|
|
base_offset = c & 127;
|
|
|
|
while (c & 128) {
|
|
|
|
base_offset += 1;
|
2007-04-09 05:06:29 +00:00
|
|
|
if (!base_offset || MSB(base_offset, 7))
|
2012-04-23 12:30:29 +00:00
|
|
|
bad_object(obj->idx.offset, _("offset value overflow for delta base object"));
|
2006-10-20 18:45:21 +00:00
|
|
|
p = fill(1);
|
|
|
|
c = *p;
|
|
|
|
use(1);
|
2006-09-21 04:08:33 +00:00
|
|
|
base_offset = (base_offset << 7) + (c & 127);
|
|
|
|
}
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
*ofs_offset = obj->idx.offset - base_offset;
|
|
|
|
if (*ofs_offset <= 0 || *ofs_offset >= obj->idx.offset)
|
2012-04-23 12:30:29 +00:00
|
|
|
bad_object(obj->idx.offset, _("delta base offset is out of bound"));
|
2006-09-21 04:08:33 +00:00
|
|
|
break;
|
2005-10-12 19:01:31 +00:00
|
|
|
case OBJ_COMMIT:
|
|
|
|
case OBJ_TREE:
|
|
|
|
case OBJ_BLOB:
|
|
|
|
case OBJ_TAG:
|
|
|
|
break;
|
|
|
|
default:
|
2012-04-23 12:30:29 +00:00
|
|
|
bad_object(obj->idx.offset, _("unknown object type %d"), obj->type);
|
2005-10-12 19:01:31 +00:00
|
|
|
}
|
2007-06-01 19:18:05 +00:00
|
|
|
obj->hdr_size = consumed_bytes - obj->idx.offset;
|
2006-10-20 18:45:21 +00:00
|
|
|
|
2018-02-01 02:18:39 +00:00
|
|
|
data = unpack_entry_data(obj->idx.offset, obj->size, obj->type, oid);
|
2007-06-01 19:18:05 +00:00
|
|
|
obj->idx.crc32 = input_crc32;
|
2007-04-09 05:06:32 +00:00
|
|
|
return data;
|
2006-10-20 18:45:21 +00:00
|
|
|
}
|
|
|
|
|
2012-05-23 14:09:48 +00:00
|
|
|
static void *unpack_data(struct object_entry *obj,
|
|
|
|
int (*consume)(const unsigned char *, unsigned long, void *),
|
|
|
|
void *cb_data)
|
2006-10-20 18:45:21 +00:00
|
|
|
{
|
2007-11-11 04:29:10 +00:00
|
|
|
off_t from = obj[0].idx.offset + obj[0].hdr_size;
|
2016-07-13 15:44:00 +00:00
|
|
|
off_t len = obj[1].idx.offset - from;
|
2010-04-12 16:11:07 +00:00
|
|
|
unsigned char *data, *inbuf;
|
2011-06-10 18:52:15 +00:00
|
|
|
git_zstream stream;
|
2010-04-12 16:11:07 +00:00
|
|
|
int status;
|
2005-10-12 19:01:31 +00:00
|
|
|
|
2014-12-08 14:17:55 +00:00
|
|
|
data = xmallocz(consume ? 64*1024 : obj->size);
|
2016-07-13 15:44:00 +00:00
|
|
|
inbuf = xmalloc((len < 64*1024) ? (int)len : 64*1024);
|
2010-04-12 16:11:07 +00:00
|
|
|
|
2006-10-20 18:45:21 +00:00
|
|
|
memset(&stream, 0, sizeof(stream));
|
2010-04-12 16:11:07 +00:00
|
|
|
git_inflate_init(&stream);
|
2006-10-20 18:45:21 +00:00
|
|
|
stream.next_out = data;
|
2012-05-23 14:09:48 +00:00
|
|
|
stream.avail_out = consume ? 64*1024 : obj->size;
|
2010-04-12 16:11:07 +00:00
|
|
|
|
|
|
|
do {
|
2016-07-13 15:44:00 +00:00
|
|
|
ssize_t n = (len < 64*1024) ? (ssize_t)len : 64*1024;
|
2014-06-03 19:06:42 +00:00
|
|
|
n = xpread(get_thread_data()->pack_fd, inbuf, n, from);
|
2010-04-12 16:11:07 +00:00
|
|
|
if (n < 0)
|
2012-04-23 12:30:29 +00:00
|
|
|
die_errno(_("cannot pread pack file"));
|
2010-04-12 16:11:07 +00:00
|
|
|
if (!n)
|
2016-07-13 15:44:00 +00:00
|
|
|
die(Q_("premature end of pack file, %"PRIuMAX" byte missing",
|
|
|
|
"premature end of pack file, %"PRIuMAX" bytes missing",
|
gettext API users: don't explicitly cast ngettext()'s "n"
Change a few stray users of the inline gettext.h Q_() function to stop
casting its "n" argument, the vast majority of the users of that
wrapper API use the implicit cast to "unsigned long".
The ngettext() function (which Q_() resolves to) takes an "unsigned
long int", and so does our Q_() wrapper for it, see 0c9ea33b90f (i18n:
add stub Q_() wrapper for ngettext, 2011-03-09). The function isn't
ours, but provided by e.g. GNU libintl.
This amends code added in added in 7171a0b0cf5 (index-pack: correct
"len" type in unpack_data(), 2016-07-13). The cast it added for the
printf format to die() was needed, but not the cast to Q_().
Likewise the casts in strbuf.c added in 8f354a1faed (l10n: localizable
upload progress messages, 2019-07-02) and for
builtin/merge-recursive.c in ccf7813139f (i18n: merge-recursive: mark
error messages for translation, 2016-09-15) weren't needed.
In the latter case the cast was copy/pasted from the argument to
warning() itself, added in b74d779bd90 (MinGW: Fix compiler warning in
merge-recursive, 2009-05-23). The cast for warning() is needed, but
not the one for ngettext()'s "n" argument.
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-03-07 15:27:07 +00:00
|
|
|
len),
|
2016-07-13 15:44:00 +00:00
|
|
|
(uintmax_t)len);
|
2010-04-12 16:11:07 +00:00
|
|
|
from += n;
|
|
|
|
len -= n;
|
|
|
|
stream.next_in = inbuf;
|
|
|
|
stream.avail_in = n;
|
index-pack: loop while inflating objects in unpack_data
When the unpack_data function is given a consume() callback,
it unpacks only 64K of the input at a time, feeding it to
git_inflate along with a 64K output buffer. However,
because we are inflating, there is a good chance that the
output buffer will fill before consuming all of the input.
In this case, we need to loop on git_inflate until we have
fed the whole input buffer, feeding each chunk of output to
the consume buffer.
The current code does not do this, and as a result, will
fail the loop condition and trigger a fatal "serious inflate
inconsistency" error in this case.
While we're rearranging the loop, let's get rid of the
extra last_out pointer. It is meant to point to the
beginning of the buffer that we feed to git_inflate, but in
practice this is always the beginning of our same 64K
buffer, because:
1. At the beginning of the loop, we are feeding the
buffer.
2. At the end of the loop, if we are using a consume()
function, we reset git_inflate's pointer to the
beginning of the buffer. If we are not using a
consume() function, then we do not care about the value
of last_out at all.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2012-07-04 07:12:14 +00:00
|
|
|
if (!consume)
|
|
|
|
status = git_inflate(&stream, 0);
|
|
|
|
else {
|
|
|
|
do {
|
|
|
|
status = git_inflate(&stream, 0);
|
|
|
|
if (consume(data, stream.next_out - data, cb_data)) {
|
|
|
|
free(inbuf);
|
|
|
|
free(data);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
stream.next_out = data;
|
|
|
|
stream.avail_out = 64*1024;
|
|
|
|
} while (status == Z_OK && stream.avail_in);
|
2012-05-23 14:09:48 +00:00
|
|
|
}
|
2010-04-12 16:11:07 +00:00
|
|
|
} while (len && status == Z_OK && !stream.avail_in);
|
|
|
|
|
|
|
|
/* This has been inflated OK when first encountered, so... */
|
|
|
|
if (status != Z_STREAM_END || stream.total_out != obj->size)
|
2012-04-23 12:30:29 +00:00
|
|
|
die(_("serious inflate inconsistency"));
|
2010-04-12 16:11:07 +00:00
|
|
|
|
|
|
|
git_inflate_end(&stream);
|
|
|
|
free(inbuf);
|
2012-05-23 14:09:48 +00:00
|
|
|
if (consume) {
|
2017-06-15 23:15:46 +00:00
|
|
|
FREE_AND_NULL(data);
|
2012-05-23 14:09:48 +00:00
|
|
|
}
|
2005-10-12 19:01:31 +00:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2012-05-23 14:09:48 +00:00
|
|
|
static void *get_data_from_pack(struct object_entry *obj)
|
|
|
|
{
|
|
|
|
return unpack_data(obj, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
static int compare_ofs_delta_bases(off_t offset1, off_t offset2,
|
|
|
|
enum object_type type1,
|
|
|
|
enum object_type type2)
|
2011-02-02 18:06:51 +00:00
|
|
|
{
|
|
|
|
int cmp = type1 - type2;
|
|
|
|
if (cmp)
|
|
|
|
return cmp;
|
index-pack: fix truncation of off_t in comparison
Commit c6458e6 (index-pack: kill union delta_base to save
memory, 2015-04-18) refactored the comparison functions used
in sorting and binary searching our delta list. The
resulting code does something like:
int cmp_offsets(off_t a, off_t b)
{
return a - b;
}
This works most of the time, but produces nonsensical
results when the difference between the two offsets is
larger than what can be stored in an "int". This can lead to
unresolved deltas if the packsize is larger than 2G (even on
64-bit systems, an int is still typically 32 bits):
$ git clone git://github.com/mozilla/gecko-dev
Cloning into 'gecko-dev'...
remote: Counting objects: 4800161, done.
remote: Compressing objects: 100% (178/178), done.
remote: Total 4800161 (delta 88), reused 0 (delta 0), pack-reused 4799978
Receiving objects: 100% (4800161/4800161), 2.21 GiB | 3.26 MiB/s, done.
Resolving deltas: 99% (3808820/3811944), completed with 0 local objects.
fatal: pack has 3124 unresolved deltas
fatal: index-pack failed
We can fix it by doing direct comparisons between the
offsets and returning constants; the callers only care about
the sign of the comparison, not the magnitude.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-06-04 12:35:42 +00:00
|
|
|
return offset1 < offset2 ? -1 :
|
|
|
|
offset1 > offset2 ? 1 :
|
|
|
|
0;
|
2011-02-02 18:06:51 +00:00
|
|
|
}
|
|
|
|
|
2020-08-24 19:16:33 +00:00
|
|
|
static int find_ofs_delta(const off_t offset)
|
2005-10-12 19:01:31 +00:00
|
|
|
{
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
int first = 0, last = nr_ofs_deltas;
|
|
|
|
|
|
|
|
while (first < last) {
|
2017-10-08 18:29:37 +00:00
|
|
|
int next = first + (last - first) / 2;
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
struct ofs_delta_entry *delta = &ofs_deltas[next];
|
|
|
|
int cmp;
|
|
|
|
|
|
|
|
cmp = compare_ofs_delta_bases(offset, delta->offset,
|
2020-08-24 19:16:33 +00:00
|
|
|
OBJ_OFS_DELTA,
|
|
|
|
objects[delta->obj_no].type);
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
if (!cmp)
|
|
|
|
return next;
|
|
|
|
if (cmp < 0) {
|
|
|
|
last = next;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
first = next+1;
|
|
|
|
}
|
|
|
|
return -first-1;
|
2005-10-12 19:01:31 +00:00
|
|
|
}
|
|
|
|
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
static void find_ofs_delta_children(off_t offset,
|
2020-08-24 19:16:33 +00:00
|
|
|
int *first_index, int *last_index)
|
2005-10-12 19:01:31 +00:00
|
|
|
{
|
2020-08-24 19:16:33 +00:00
|
|
|
int first = find_ofs_delta(offset);
|
2005-10-12 19:01:31 +00:00
|
|
|
int last = first;
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
int end = nr_ofs_deltas - 1;
|
2005-10-12 19:01:31 +00:00
|
|
|
|
2008-10-17 19:57:58 +00:00
|
|
|
if (first < 0) {
|
|
|
|
*first_index = 0;
|
|
|
|
*last_index = -1;
|
|
|
|
return;
|
|
|
|
}
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
while (first > 0 && ofs_deltas[first - 1].offset == offset)
|
2005-10-12 19:01:31 +00:00
|
|
|
--first;
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
while (last < end && ofs_deltas[last + 1].offset == offset)
|
|
|
|
++last;
|
|
|
|
*first_index = first;
|
|
|
|
*last_index = last;
|
|
|
|
}
|
|
|
|
|
2018-03-12 02:27:37 +00:00
|
|
|
static int compare_ref_delta_bases(const struct object_id *oid1,
|
|
|
|
const struct object_id *oid2,
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
enum object_type type1,
|
|
|
|
enum object_type type2)
|
|
|
|
{
|
|
|
|
int cmp = type1 - type2;
|
|
|
|
if (cmp)
|
|
|
|
return cmp;
|
2018-03-12 02:27:37 +00:00
|
|
|
return oidcmp(oid1, oid2);
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
}
|
|
|
|
|
2020-08-24 19:16:33 +00:00
|
|
|
static int find_ref_delta(const struct object_id *oid)
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
{
|
|
|
|
int first = 0, last = nr_ref_deltas;
|
|
|
|
|
|
|
|
while (first < last) {
|
2017-10-08 18:29:37 +00:00
|
|
|
int next = first + (last - first) / 2;
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
struct ref_delta_entry *delta = &ref_deltas[next];
|
|
|
|
int cmp;
|
|
|
|
|
2018-03-12 02:27:37 +00:00
|
|
|
cmp = compare_ref_delta_bases(oid, &delta->oid,
|
2020-08-24 19:16:33 +00:00
|
|
|
OBJ_REF_DELTA,
|
|
|
|
objects[delta->obj_no].type);
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
if (!cmp)
|
|
|
|
return next;
|
|
|
|
if (cmp < 0) {
|
|
|
|
last = next;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
first = next+1;
|
|
|
|
}
|
|
|
|
return -first-1;
|
|
|
|
}
|
|
|
|
|
2018-03-12 02:27:37 +00:00
|
|
|
static void find_ref_delta_children(const struct object_id *oid,
|
2020-08-24 19:16:33 +00:00
|
|
|
int *first_index, int *last_index)
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
{
|
2020-08-24 19:16:33 +00:00
|
|
|
int first = find_ref_delta(oid);
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
int last = first;
|
|
|
|
int end = nr_ref_deltas - 1;
|
|
|
|
|
|
|
|
if (first < 0) {
|
|
|
|
*first_index = 0;
|
|
|
|
*last_index = -1;
|
|
|
|
return;
|
|
|
|
}
|
convert "oidcmp() == 0" to oideq()
Using the more restrictive oideq() should, in the long run,
give the compiler more opportunities to optimize these
callsites. For now, this conversion should be a complete
noop with respect to the generated code.
The result is also perhaps a little more readable, as it
avoids the "zero is equal" idiom. Since it's so prevalent in
C, I think seasoned programmers tend not to even notice it
anymore, but it can sometimes make for awkward double
negations (e.g., we can drop a few !!oidcmp() instances
here).
This patch was generated almost entirely by the included
coccinelle patch. This mechanical conversion should be
completely safe, because we check explicitly for cases where
oidcmp() is compared to 0, which is what oideq() is doing
under the hood. Note that we don't have to catch "!oidcmp()"
separately; coccinelle's standard isomorphisms make sure the
two are treated equivalently.
I say "almost" because I did hand-edit the coccinelle output
to fix up a few style violations (it mostly keeps the
original formatting, but sometimes unwraps long lines).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-28 21:22:40 +00:00
|
|
|
while (first > 0 && oideq(&ref_deltas[first - 1].oid, oid))
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
--first;
|
convert "oidcmp() == 0" to oideq()
Using the more restrictive oideq() should, in the long run,
give the compiler more opportunities to optimize these
callsites. For now, this conversion should be a complete
noop with respect to the generated code.
The result is also perhaps a little more readable, as it
avoids the "zero is equal" idiom. Since it's so prevalent in
C, I think seasoned programmers tend not to even notice it
anymore, but it can sometimes make for awkward double
negations (e.g., we can drop a few !!oidcmp() instances
here).
This patch was generated almost entirely by the included
coccinelle patch. This mechanical conversion should be
completely safe, because we check explicitly for cases where
oidcmp() is compared to 0, which is what oideq() is doing
under the hood. Note that we don't have to catch "!oidcmp()"
separately; coccinelle's standard isomorphisms make sure the
two are treated equivalently.
I say "almost" because I did hand-edit the coccinelle output
to fix up a few style violations (it mostly keeps the
original formatting, but sometimes unwraps long lines).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-28 21:22:40 +00:00
|
|
|
while (last < end && oideq(&ref_deltas[last + 1].oid, oid))
|
2005-10-12 19:01:31 +00:00
|
|
|
++last;
|
|
|
|
*first_index = first;
|
|
|
|
*last_index = last;
|
|
|
|
}
|
|
|
|
|
2012-05-24 13:55:44 +00:00
|
|
|
struct compare_data {
|
|
|
|
struct object_entry *entry;
|
|
|
|
struct git_istream *st;
|
|
|
|
unsigned char *buf;
|
|
|
|
unsigned long buf_size;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int compare_objects(const unsigned char *buf, unsigned long size,
|
|
|
|
void *cb_data)
|
|
|
|
{
|
|
|
|
struct compare_data *data = cb_data;
|
|
|
|
|
|
|
|
if (data->buf_size < size) {
|
|
|
|
free(data->buf);
|
|
|
|
data->buf = xmalloc(size);
|
|
|
|
data->buf_size = size;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (size) {
|
|
|
|
ssize_t len = read_istream(data->st, data->buf, size);
|
|
|
|
if (len == 0)
|
|
|
|
die(_("SHA1 COLLISION FOUND WITH %s !"),
|
2017-05-06 22:10:11 +00:00
|
|
|
oid_to_hex(&data->entry->idx.oid));
|
2012-05-24 13:55:44 +00:00
|
|
|
if (len < 0)
|
|
|
|
die(_("unable to read %s"),
|
2017-05-06 22:10:11 +00:00
|
|
|
oid_to_hex(&data->entry->idx.oid));
|
2012-05-24 13:55:44 +00:00
|
|
|
if (memcmp(buf, data->buf, len))
|
|
|
|
die(_("SHA1 COLLISION FOUND WITH %s !"),
|
2017-05-06 22:10:11 +00:00
|
|
|
oid_to_hex(&data->entry->idx.oid));
|
2012-05-24 13:55:44 +00:00
|
|
|
size -= len;
|
|
|
|
buf += len;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int check_collison(struct object_entry *entry)
|
|
|
|
{
|
|
|
|
struct compare_data data;
|
|
|
|
enum object_type type;
|
|
|
|
unsigned long size;
|
|
|
|
|
|
|
|
if (entry->size <= big_file_threshold || entry->type != OBJ_BLOB)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
memset(&data, 0, sizeof(data));
|
|
|
|
data.entry = entry;
|
2020-01-30 20:32:20 +00:00
|
|
|
data.st = open_istream(the_repository, &entry->idx.oid, &type, &size,
|
|
|
|
NULL);
|
2012-05-24 13:55:44 +00:00
|
|
|
if (!data.st)
|
|
|
|
return -1;
|
|
|
|
if (size != entry->size || type != entry->type)
|
|
|
|
die(_("SHA1 COLLISION FOUND WITH %s !"),
|
2017-05-06 22:10:11 +00:00
|
|
|
oid_to_hex(&entry->idx.oid));
|
2012-05-24 13:55:44 +00:00
|
|
|
unpack_data(entry, compare_objects, &data);
|
|
|
|
close_istream(data.st);
|
|
|
|
free(data.buf);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-05-23 14:09:47 +00:00
|
|
|
static void sha1_object(const void *data, struct object_entry *obj_entry,
|
|
|
|
unsigned long size, enum object_type type,
|
2017-05-06 22:10:13 +00:00
|
|
|
const struct object_id *oid)
|
2005-10-12 19:01:31 +00:00
|
|
|
{
|
2012-05-23 14:09:47 +00:00
|
|
|
void *new_data = NULL;
|
2016-12-16 21:43:22 +00:00
|
|
|
int collision_test_needed = 0;
|
2012-05-23 14:09:47 +00:00
|
|
|
|
|
|
|
assert(data || obj_entry);
|
|
|
|
|
2016-12-16 21:43:22 +00:00
|
|
|
if (startup_info->have_repository) {
|
|
|
|
read_lock();
|
2017-06-22 00:40:24 +00:00
|
|
|
collision_test_needed =
|
2023-03-28 13:58:50 +00:00
|
|
|
repo_has_object_file_with_flags(the_repository, oid,
|
|
|
|
OBJECT_INFO_QUICK);
|
2016-12-16 21:43:22 +00:00
|
|
|
read_unlock();
|
|
|
|
}
|
2012-05-24 13:55:44 +00:00
|
|
|
|
|
|
|
if (collision_test_needed && !data) {
|
|
|
|
read_lock();
|
|
|
|
if (!check_collison(obj_entry))
|
|
|
|
collision_test_needed = 0;
|
|
|
|
read_unlock();
|
|
|
|
}
|
|
|
|
if (collision_test_needed) {
|
2007-03-20 19:32:35 +00:00
|
|
|
void *has_data;
|
|
|
|
enum object_type has_type;
|
|
|
|
unsigned long has_size;
|
2012-05-24 13:55:44 +00:00
|
|
|
read_lock();
|
2018-04-25 18:20:59 +00:00
|
|
|
has_type = oid_object_info(the_repository, oid, &has_size);
|
index-pack: detect local corruption in collision check
When we notice that we have a local copy of an incoming
object, we compare the two objects to make sure we haven't
found a collision. Before we get to the actual object
bytes, though, we compare the type and size from
sha1_object_info().
If our local object is corrupted, then the type will be
OBJ_BAD, which obviously will not match the incoming type,
and we'll report "SHA1 COLLISION FOUND" (with capital
letters and everything). This is confusing, as the problem
is not a collision but rather local corruption. We should
report that instead (just like we do if reading the rest of
the object content fails a few lines later).
Note that we _could_ just ignore the error and mark it as a
non-collision. That would let you "git fetch" to replace a
corrupted object. But it's not a very reliable method for
repairing a repository. The earlier want/have negotiation
tries to get the other side to omit objects we already have,
and it would not realize that we are "missing" this
corrupted object. So we're better off complaining loudly
when we see corruption, and letting the user take more
drastic measures to repair (like making a full clone
elsewhere and copying the pack into place).
Note that the test sets transfer.unpackLimit in the
receiving repository so that we use index-pack (which is
what does the collision check). Normally for such a small
push we'd use unpack-objects, which would simply try to
write the loose object, and discard the new one when we see
that there's already an old one.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-04-01 08:09:32 +00:00
|
|
|
if (has_type < 0)
|
2017-05-06 22:10:13 +00:00
|
|
|
die(_("cannot read existing object info %s"), oid_to_hex(oid));
|
2012-05-24 13:55:44 +00:00
|
|
|
if (has_type != type || has_size != size)
|
2017-05-06 22:10:13 +00:00
|
|
|
die(_("SHA1 COLLISION FOUND WITH %s !"), oid_to_hex(oid));
|
2023-03-28 13:58:50 +00:00
|
|
|
has_data = repo_read_object_file(the_repository, oid,
|
|
|
|
&has_type, &has_size);
|
2012-05-06 12:31:55 +00:00
|
|
|
read_unlock();
|
2012-05-24 13:55:44 +00:00
|
|
|
if (!data)
|
|
|
|
data = new_data = get_data_from_pack(obj_entry);
|
2007-03-20 19:32:35 +00:00
|
|
|
if (!has_data)
|
2017-05-06 22:10:13 +00:00
|
|
|
die(_("cannot read existing object %s"), oid_to_hex(oid));
|
2007-03-20 19:32:35 +00:00
|
|
|
if (size != has_size || type != has_type ||
|
|
|
|
memcmp(data, has_data, size) != 0)
|
2017-05-06 22:10:13 +00:00
|
|
|
die(_("SHA1 COLLISION FOUND WITH %s !"), oid_to_hex(oid));
|
2007-04-03 16:33:46 +00:00
|
|
|
free(has_data);
|
2012-05-24 13:55:44 +00:00
|
|
|
}
|
2012-05-06 12:31:55 +00:00
|
|
|
|
2018-03-14 18:42:40 +00:00
|
|
|
if (strict || do_fsck_object) {
|
2012-05-06 12:31:55 +00:00
|
|
|
read_lock();
|
2008-02-25 21:46:12 +00:00
|
|
|
if (type == OBJ_BLOB) {
|
2018-06-29 01:21:55 +00:00
|
|
|
struct blob *blob = lookup_blob(the_repository, oid);
|
2008-02-25 21:46:12 +00:00
|
|
|
if (blob)
|
|
|
|
blob->object.flags |= FLAG_CHECKED;
|
|
|
|
else
|
2017-05-06 22:10:13 +00:00
|
|
|
die(_("invalid blob object %s"), oid_to_hex(oid));
|
index-pack: check .gitmodules files with --strict
Now that the internal fsck code has all of the plumbing we
need, we can start checking incoming .gitmodules files.
Naively, it seems like we would just need to add a call to
fsck_finish() after we've processed all of the objects. And
that would be enough to cover the initial test included
here. But there are two extra bits:
1. We currently don't bother calling fsck_object() at all
for blobs, since it has traditionally been a noop. We'd
actually catch these blobs in fsck_finish() at the end,
but it's more efficient to check them when we already
have the object loaded in memory.
2. The second pass done by fsck_finish() needs to access
the objects, but we're actually indexing the pack in
this process. In theory we could give the fsck code a
special callback for accessing the in-pack data, but
it's actually quite tricky:
a. We don't have an internal efficient index mapping
oids to packfile offsets. We only generate it on
the fly as part of writing out the .idx file.
b. We'd still have to reconstruct deltas, which means
we'd basically have to replicate all of the
reading logic in packfile.c.
Instead, let's avoid running fsck_finish() until after
we've written out the .idx file, and then just add it
to our internal packed_git list.
This does mean that the objects are "in the repository"
before we finish our fsck checks. But unpack-objects
already exhibits this same behavior, and it's an
acceptable tradeoff here for the same reason: the
quarantine mechanism means that pushes will be
fully protected.
In addition to a basic push test in t7415, we add a sneaky
pack that reverses the usual object order in the pack,
requiring that index-pack access the tree and blob during
the "finish" step.
This already works for unpack-objects (since it will have
written out loose objects), but we'll check it with this
sneaky pack for good measure.
Signed-off-by: Jeff King <peff@peff.net>
2018-05-04 23:45:01 +00:00
|
|
|
if (do_fsck_object &&
|
|
|
|
fsck_object(&blob->object, (void *)data, size, &fsck_options))
|
|
|
|
die(_("fsck error in packed object"));
|
2008-02-25 21:46:12 +00:00
|
|
|
} else {
|
|
|
|
struct object *obj;
|
|
|
|
int eaten;
|
|
|
|
void *buf = (void *) data;
|
|
|
|
|
2013-05-26 01:16:16 +00:00
|
|
|
assert(data && "data can only be NULL for large _blobs_");
|
2012-05-23 14:09:47 +00:00
|
|
|
|
2008-02-25 21:46:12 +00:00
|
|
|
/*
|
|
|
|
* we do not need to free the memory here, as the
|
|
|
|
* buf is deleted by the caller.
|
|
|
|
*/
|
2018-06-29 01:21:53 +00:00
|
|
|
obj = parse_object_buffer(the_repository, oid, type,
|
|
|
|
size, buf,
|
object: convert parse_object* to take struct object_id
Make parse_object, parse_object_or_die, and parse_object_buffer take a
pointer to struct object_id. Remove the temporary variables inserted
earlier, since they are no longer necessary. Transform all of the
callers using the following semantic patch:
@@
expression E1;
@@
- parse_object(E1.hash)
+ parse_object(&E1)
@@
expression E1;
@@
- parse_object(E1->hash)
+ parse_object(E1)
@@
expression E1, E2;
@@
- parse_object_or_die(E1.hash, E2)
+ parse_object_or_die(&E1, E2)
@@
expression E1, E2;
@@
- parse_object_or_die(E1->hash, E2)
+ parse_object_or_die(E1, E2)
@@
expression E1, E2, E3, E4, E5;
@@
- parse_object_buffer(E1.hash, E2, E3, E4, E5)
+ parse_object_buffer(&E1, E2, E3, E4, E5)
@@
expression E1, E2, E3, E4, E5;
@@
- parse_object_buffer(E1->hash, E2, E3, E4, E5)
+ parse_object_buffer(E1, E2, E3, E4, E5)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-06 22:10:38 +00:00
|
|
|
&eaten);
|
2008-02-25 21:46:12 +00:00
|
|
|
if (!obj)
|
2018-02-14 18:59:24 +00:00
|
|
|
die(_("invalid %s"), type_name(type));
|
2013-05-26 01:16:17 +00:00
|
|
|
if (do_fsck_object &&
|
2015-06-22 15:25:00 +00:00
|
|
|
fsck_object(obj, buf, size, &fsck_options))
|
2018-05-02 20:37:09 +00:00
|
|
|
die(_("fsck error in packed object"));
|
2018-03-14 18:42:40 +00:00
|
|
|
if (strict && fsck_walk(obj, NULL, &fsck_options))
|
2015-11-10 02:22:28 +00:00
|
|
|
die(_("Not all child objects of %s are reachable"), oid_to_hex(&obj->oid));
|
2008-02-25 21:46:12 +00:00
|
|
|
|
|
|
|
if (obj->type == OBJ_TREE) {
|
|
|
|
struct tree *item = (struct tree *) obj;
|
|
|
|
item->buffer = NULL;
|
2013-06-05 22:37:39 +00:00
|
|
|
obj->parsed = 0;
|
2008-02-25 21:46:12 +00:00
|
|
|
}
|
|
|
|
if (obj->type == OBJ_COMMIT) {
|
|
|
|
struct commit *commit = (struct commit *) obj;
|
2014-06-10 21:44:13 +00:00
|
|
|
if (detach_commit_buffer(commit, NULL) != data)
|
2018-05-02 09:38:39 +00:00
|
|
|
BUG("parse_object_buffer transmogrified our buffer");
|
2008-02-25 21:46:12 +00:00
|
|
|
}
|
|
|
|
obj->flags |= FLAG_CHECKED;
|
|
|
|
}
|
2012-05-06 12:31:55 +00:00
|
|
|
read_unlock();
|
2008-02-25 21:46:12 +00:00
|
|
|
}
|
2012-05-23 14:09:47 +00:00
|
|
|
|
|
|
|
free(new_data);
|
2005-10-12 19:01:31 +00:00
|
|
|
}
|
|
|
|
|
2012-01-14 12:19:55 +00:00
|
|
|
/*
|
2020-10-07 20:16:58 +00:00
|
|
|
* Ensure that this node has been reconstructed and return its contents.
|
2012-01-14 12:19:55 +00:00
|
|
|
*
|
2020-10-07 20:16:58 +00:00
|
|
|
* In the typical and best case, this node would already be reconstructed
|
|
|
|
* (through the invocation to resolve_delta() in threaded_second_pass()) and it
|
|
|
|
* would not be pruned. However, if pruning of this node was necessary due to
|
|
|
|
* reaching delta_base_cache_limit, this function will find the closest
|
|
|
|
* ancestor with reconstructed data that has not been pruned (or if there is
|
|
|
|
* none, the ultimate base object), and reconstruct each node in the delta
|
|
|
|
* chain in order to generate the reconstructed data for this node.
|
2012-01-14 12:19:55 +00:00
|
|
|
*/
|
2008-07-15 04:45:34 +00:00
|
|
|
static void *get_base_data(struct base_data *c)
|
|
|
|
{
|
|
|
|
if (!c->data) {
|
|
|
|
struct object_entry *obj = c->obj;
|
2012-01-14 12:19:55 +00:00
|
|
|
struct base_data **delta = NULL;
|
|
|
|
int delta_nr = 0, delta_alloc = 0;
|
2008-07-15 04:45:34 +00:00
|
|
|
|
2012-01-14 12:19:55 +00:00
|
|
|
while (is_delta_type(c->obj->type) && !c->data) {
|
|
|
|
ALLOC_GROW(delta, delta_nr + 1, delta_alloc);
|
|
|
|
delta[delta_nr++] = c;
|
|
|
|
c = c->base;
|
|
|
|
}
|
|
|
|
if (!delta_nr) {
|
|
|
|
c->data = get_data_from_pack(obj);
|
|
|
|
c->size = obj->size;
|
index-pack: make quantum of work smaller
Currently, when index-pack resolves deltas, it does not split up delta
trees into threads: each delta base root (an object that is not a
REF_DELTA or OFS_DELTA) can go into its own thread, but all deltas on
that root (direct or indirect) are processed in the same thread.
This is a problem when a repository contains a large text file (thus,
delta-able) that is modified many times - delta resolution time during
fetching is dominated by processing the deltas corresponding to that
text file.
This patch contains a solution to that. When cloning using
git -c core.deltabasecachelimit=1g clone \
https://fuchsia.googlesource.com/third_party/vulkan-cts
on my laptop, clone time improved from 3m2s to 2m5s (using 3 threads,
which is the default).
The solution is to have a global work stack. This stack contains delta
bases (objects, whether appearing directly in the packfile or generated
by delta resolution, that themselves have delta children) that need to
be processed; whenever a thread needs work, it peeks at the top of the
stack and processes its next unprocessed child. If a thread finds the
stack empty, it will look for more delta base roots to push on the stack
instead.
The main weakness of having a global work stack is that more time is
spent in the mutex, but profiling has shown that most time is spent in
the resolution of the deltas themselves, so this shouldn't be an issue
in practice. In any case, experimentation (as described in the clone
command above) shows that this patch is a net improvement.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-09-08 19:48:35 +00:00
|
|
|
base_cache_used += c->size;
|
2012-01-14 12:19:55 +00:00
|
|
|
prune_base_data(c);
|
|
|
|
}
|
|
|
|
for (; delta_nr > 0; delta_nr--) {
|
|
|
|
void *base, *raw;
|
|
|
|
c = delta[delta_nr - 1];
|
|
|
|
obj = c->obj;
|
|
|
|
base = get_base_data(c->base);
|
|
|
|
raw = get_data_from_pack(obj);
|
2008-07-15 04:45:34 +00:00
|
|
|
c->data = patch_delta(
|
|
|
|
base, c->base->size,
|
|
|
|
raw, obj->size,
|
|
|
|
&c->size);
|
|
|
|
free(raw);
|
|
|
|
if (!c->data)
|
2012-04-23 12:30:29 +00:00
|
|
|
bad_object(obj->idx.offset, _("failed to apply delta"));
|
index-pack: make quantum of work smaller
Currently, when index-pack resolves deltas, it does not split up delta
trees into threads: each delta base root (an object that is not a
REF_DELTA or OFS_DELTA) can go into its own thread, but all deltas on
that root (direct or indirect) are processed in the same thread.
This is a problem when a repository contains a large text file (thus,
delta-able) that is modified many times - delta resolution time during
fetching is dominated by processing the deltas corresponding to that
text file.
This patch contains a solution to that. When cloning using
git -c core.deltabasecachelimit=1g clone \
https://fuchsia.googlesource.com/third_party/vulkan-cts
on my laptop, clone time improved from 3m2s to 2m5s (using 3 threads,
which is the default).
The solution is to have a global work stack. This stack contains delta
bases (objects, whether appearing directly in the packfile or generated
by delta resolution, that themselves have delta children) that need to
be processed; whenever a thread needs work, it peeks at the top of the
stack and processes its next unprocessed child. If a thread finds the
stack empty, it will look for more delta base roots to push on the stack
instead.
The main weakness of having a global work stack is that more time is
spent in the mutex, but profiling has shown that most time is spent in
the resolution of the deltas themselves, so this shouldn't be an issue
in practice. In any case, experimentation (as described in the clone
command above) shows that this patch is a net improvement.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-09-08 19:48:35 +00:00
|
|
|
base_cache_used += c->size;
|
2012-01-14 12:19:55 +00:00
|
|
|
prune_base_data(c);
|
2008-10-17 19:57:57 +00:00
|
|
|
}
|
2012-01-14 12:19:55 +00:00
|
|
|
free(delta);
|
2008-07-15 04:45:34 +00:00
|
|
|
}
|
|
|
|
return c->data;
|
|
|
|
}
|
|
|
|
|
2020-08-24 19:16:36 +00:00
|
|
|
static struct base_data *make_base(struct object_entry *obj,
|
|
|
|
struct base_data *parent)
|
2005-10-12 19:01:31 +00:00
|
|
|
{
|
2020-08-24 19:16:36 +00:00
|
|
|
struct base_data *base = xcalloc(1, sizeof(struct base_data));
|
|
|
|
base->base = parent;
|
|
|
|
base->obj = obj;
|
|
|
|
find_ref_delta_children(&obj->idx.oid,
|
|
|
|
&base->ref_first, &base->ref_last);
|
|
|
|
find_ofs_delta_children(obj->idx.offset,
|
|
|
|
&base->ofs_first, &base->ofs_last);
|
index-pack: make quantum of work smaller
Currently, when index-pack resolves deltas, it does not split up delta
trees into threads: each delta base root (an object that is not a
REF_DELTA or OFS_DELTA) can go into its own thread, but all deltas on
that root (direct or indirect) are processed in the same thread.
This is a problem when a repository contains a large text file (thus,
delta-able) that is modified many times - delta resolution time during
fetching is dominated by processing the deltas corresponding to that
text file.
This patch contains a solution to that. When cloning using
git -c core.deltabasecachelimit=1g clone \
https://fuchsia.googlesource.com/third_party/vulkan-cts
on my laptop, clone time improved from 3m2s to 2m5s (using 3 threads,
which is the default).
The solution is to have a global work stack. This stack contains delta
bases (objects, whether appearing directly in the packfile or generated
by delta resolution, that themselves have delta children) that need to
be processed; whenever a thread needs work, it peeks at the top of the
stack and processes its next unprocessed child. If a thread finds the
stack empty, it will look for more delta base roots to push on the stack
instead.
The main weakness of having a global work stack is that more time is
spent in the mutex, but profiling has shown that most time is spent in
the resolution of the deltas themselves, so this shouldn't be an issue
in practice. In any case, experimentation (as described in the clone
command above) shows that this patch is a net improvement.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-09-08 19:48:35 +00:00
|
|
|
base->children_remaining = base->ref_last - base->ref_first +
|
|
|
|
base->ofs_last - base->ofs_first + 2;
|
2020-08-24 19:16:36 +00:00
|
|
|
return base;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct base_data *resolve_delta(struct object_entry *delta_obj,
|
|
|
|
struct base_data *base)
|
|
|
|
{
|
2020-08-24 19:16:37 +00:00
|
|
|
void *delta_data, *result_data;
|
2020-08-24 19:16:36 +00:00
|
|
|
struct base_data *result;
|
|
|
|
unsigned long result_size;
|
2005-10-12 19:01:31 +00:00
|
|
|
|
2013-03-19 13:01:15 +00:00
|
|
|
if (show_stat) {
|
2015-02-26 10:52:07 +00:00
|
|
|
int i = delta_obj - objects;
|
|
|
|
int j = base->obj - objects;
|
|
|
|
obj_stat[i].delta_depth = obj_stat[j].delta_depth + 1;
|
2013-03-19 13:01:15 +00:00
|
|
|
deepest_delta_lock();
|
2015-02-26 10:52:07 +00:00
|
|
|
if (deepest_delta < obj_stat[i].delta_depth)
|
|
|
|
deepest_delta = obj_stat[i].delta_depth;
|
2013-03-19 13:01:15 +00:00
|
|
|
deepest_delta_unlock();
|
2015-02-26 10:52:07 +00:00
|
|
|
obj_stat[i].base_object_no = j;
|
2013-03-19 13:01:15 +00:00
|
|
|
}
|
2006-10-26 03:28:17 +00:00
|
|
|
delta_data = get_data_from_pack(delta_obj);
|
2020-08-24 19:16:37 +00:00
|
|
|
assert(base->data);
|
|
|
|
result_data = patch_delta(base->data, base->size,
|
2020-08-24 19:16:36 +00:00
|
|
|
delta_data, delta_obj->size, &result_size);
|
2005-10-12 19:01:31 +00:00
|
|
|
free(delta_data);
|
2020-08-24 19:16:36 +00:00
|
|
|
if (!result_data)
|
2012-04-23 12:30:29 +00:00
|
|
|
bad_object(delta_obj->idx.offset, _("failed to apply delta"));
|
2020-08-24 19:16:36 +00:00
|
|
|
hash_object_file(the_hash_algo, result_data, result_size,
|
2022-02-04 23:48:32 +00:00
|
|
|
delta_obj->real_type, &delta_obj->idx.oid);
|
2020-08-24 19:16:36 +00:00
|
|
|
sha1_object(result_data, NULL, result_size, delta_obj->real_type,
|
2017-05-06 22:10:13 +00:00
|
|
|
&delta_obj->idx.oid);
|
2020-08-24 19:16:36 +00:00
|
|
|
|
|
|
|
result = make_base(delta_obj, base);
|
index-pack: make quantum of work smaller
Currently, when index-pack resolves deltas, it does not split up delta
trees into threads: each delta base root (an object that is not a
REF_DELTA or OFS_DELTA) can go into its own thread, but all deltas on
that root (direct or indirect) are processed in the same thread.
This is a problem when a repository contains a large text file (thus,
delta-able) that is modified many times - delta resolution time during
fetching is dominated by processing the deltas corresponding to that
text file.
This patch contains a solution to that. When cloning using
git -c core.deltabasecachelimit=1g clone \
https://fuchsia.googlesource.com/third_party/vulkan-cts
on my laptop, clone time improved from 3m2s to 2m5s (using 3 threads,
which is the default).
The solution is to have a global work stack. This stack contains delta
bases (objects, whether appearing directly in the packfile or generated
by delta resolution, that themselves have delta children) that need to
be processed; whenever a thread needs work, it peeks at the top of the
stack and processes its next unprocessed child. If a thread finds the
stack empty, it will look for more delta base roots to push on the stack
instead.
The main weakness of having a global work stack is that more time is
spent in the mutex, but profiling has shown that most time is spent in
the resolution of the deltas themselves, so this shouldn't be an issue
in practice. In any case, experimentation (as described in the clone
command above) shows that this patch is a net improvement.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-09-08 19:48:35 +00:00
|
|
|
result->data = result_data;
|
|
|
|
result->size = result_size;
|
2020-08-24 19:16:36 +00:00
|
|
|
|
2012-05-06 12:31:55 +00:00
|
|
|
counter_lock();
|
2006-10-26 03:28:17 +00:00
|
|
|
nr_resolved_deltas++;
|
2012-05-06 12:31:55 +00:00
|
|
|
counter_unlock();
|
index-pack: fix race condition with duplicate bases
When we are resolving deltas in an indexed pack, we do it by
first selecting a potential base (either one stored in full
in the pack, or one created by resolving another delta), and
then resolving any deltas that use that base. When we
resolve a particular delta, we flip its "real_type" field
from OBJ_{REF,OFS}_DELTA to whatever the real type is.
We assume that traversing the objects this way will visit
each delta only once. This is correct for most packs; we
visit the delta only when we process its base, and each
object (and thus each base) appears only once. However, if a
base object appears multiple times in the pack, we will try
to resolve any deltas based on it once for each instance.
We can detect this case by noting that a delta we are about
to resolve has already had its real_type field flipped, and
we already do so with an assert(). However, if multiple
threads are in use, we may race with another thread on
comparing and flipping the field. We need to synchronize the
access.
The right mechanism for doing this is a compare-and-swap (we
atomically "claim" the delta for our own and find out
whether our claim was successful). We can implement this
in C by using a pthread mutex to protect the operation. This
is not the fastest way of doing a compare-and-swap; many
processors provide instructions for this, and gcc and other
compilers provide builtins to access them. However, some
experiments showed that lock contention does not cause a
significant slowdown here. Adding c-a-s support for many
compilers would increase the maintenance burden (and we
would still end up including the pthread version as a
fallback).
Note that we only need to touch the OBJ_REF_DELTA codepath
here. An OBJ_OFS_DELTA object points to its base using an
offset, and therefore has only one base, even if another
copy of that base object appears in the pack (we do still
touch it briefly because the setting of real_type is
factored out of resolve_data).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-08-29 20:57:47 +00:00
|
|
|
|
2020-08-24 19:16:36 +00:00
|
|
|
return result;
|
2005-10-12 19:01:31 +00:00
|
|
|
}
|
|
|
|
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
static int compare_ofs_delta_entry(const void *a, const void *b)
|
2005-10-12 19:01:31 +00:00
|
|
|
{
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
const struct ofs_delta_entry *delta_a = a;
|
|
|
|
const struct ofs_delta_entry *delta_b = b;
|
2011-02-02 18:06:51 +00:00
|
|
|
|
index-pack: fix truncation of off_t in comparison
Commit c6458e6 (index-pack: kill union delta_base to save
memory, 2015-04-18) refactored the comparison functions used
in sorting and binary searching our delta list. The
resulting code does something like:
int cmp_offsets(off_t a, off_t b)
{
return a - b;
}
This works most of the time, but produces nonsensical
results when the difference between the two offsets is
larger than what can be stored in an "int". This can lead to
unresolved deltas if the packsize is larger than 2G (even on
64-bit systems, an int is still typically 32 bits):
$ git clone git://github.com/mozilla/gecko-dev
Cloning into 'gecko-dev'...
remote: Counting objects: 4800161, done.
remote: Compressing objects: 100% (178/178), done.
remote: Total 4800161 (delta 88), reused 0 (delta 0), pack-reused 4799978
Receiving objects: 100% (4800161/4800161), 2.21 GiB | 3.26 MiB/s, done.
Resolving deltas: 99% (3808820/3811944), completed with 0 local objects.
fatal: pack has 3124 unresolved deltas
fatal: index-pack failed
We can fix it by doing direct comparisons between the
offsets and returning constants; the callers only care about
the sign of the comparison, not the magnitude.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-06-04 12:35:42 +00:00
|
|
|
return delta_a->offset < delta_b->offset ? -1 :
|
|
|
|
delta_a->offset > delta_b->offset ? 1 :
|
|
|
|
0;
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int compare_ref_delta_entry(const void *a, const void *b)
|
2005-10-12 19:01:31 +00:00
|
|
|
{
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
const struct ref_delta_entry *delta_a = a;
|
|
|
|
const struct ref_delta_entry *delta_b = b;
|
2011-02-02 18:06:51 +00:00
|
|
|
|
2018-03-12 02:27:37 +00:00
|
|
|
return oidcmp(&delta_a->oid, &delta_b->oid);
|
2005-10-12 19:01:31 +00:00
|
|
|
}
|
|
|
|
|
2012-05-06 12:31:55 +00:00
|
|
|
static void *threaded_second_pass(void *data)
|
|
|
|
{
|
index-pack: make quantum of work smaller
Currently, when index-pack resolves deltas, it does not split up delta
trees into threads: each delta base root (an object that is not a
REF_DELTA or OFS_DELTA) can go into its own thread, but all deltas on
that root (direct or indirect) are processed in the same thread.
This is a problem when a repository contains a large text file (thus,
delta-able) that is modified many times - delta resolution time during
fetching is dominated by processing the deltas corresponding to that
text file.
This patch contains a solution to that. When cloning using
git -c core.deltabasecachelimit=1g clone \
https://fuchsia.googlesource.com/third_party/vulkan-cts
on my laptop, clone time improved from 3m2s to 2m5s (using 3 threads,
which is the default).
The solution is to have a global work stack. This stack contains delta
bases (objects, whether appearing directly in the packfile or generated
by delta resolution, that themselves have delta children) that need to
be processed; whenever a thread needs work, it peeks at the top of the
stack and processes its next unprocessed child. If a thread finds the
stack empty, it will look for more delta base roots to push on the stack
instead.
The main weakness of having a global work stack is that more time is
spent in the mutex, but profiling has shown that most time is spent in
the resolution of the deltas themselves, so this shouldn't be an issue
in practice. In any case, experimentation (as described in the clone
command above) shows that this patch is a net improvement.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-09-08 19:48:35 +00:00
|
|
|
if (data)
|
|
|
|
set_thread_data(data);
|
2012-05-06 12:31:55 +00:00
|
|
|
for (;;) {
|
index-pack: make quantum of work smaller
Currently, when index-pack resolves deltas, it does not split up delta
trees into threads: each delta base root (an object that is not a
REF_DELTA or OFS_DELTA) can go into its own thread, but all deltas on
that root (direct or indirect) are processed in the same thread.
This is a problem when a repository contains a large text file (thus,
delta-able) that is modified many times - delta resolution time during
fetching is dominated by processing the deltas corresponding to that
text file.
This patch contains a solution to that. When cloning using
git -c core.deltabasecachelimit=1g clone \
https://fuchsia.googlesource.com/third_party/vulkan-cts
on my laptop, clone time improved from 3m2s to 2m5s (using 3 threads,
which is the default).
The solution is to have a global work stack. This stack contains delta
bases (objects, whether appearing directly in the packfile or generated
by delta resolution, that themselves have delta children) that need to
be processed; whenever a thread needs work, it peeks at the top of the
stack and processes its next unprocessed child. If a thread finds the
stack empty, it will look for more delta base roots to push on the stack
instead.
The main weakness of having a global work stack is that more time is
spent in the mutex, but profiling has shown that most time is spent in
the resolution of the deltas themselves, so this shouldn't be an issue
in practice. In any case, experimentation (as described in the clone
command above) shows that this patch is a net improvement.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-09-08 19:48:35 +00:00
|
|
|
struct base_data *parent = NULL;
|
|
|
|
struct object_entry *child_obj;
|
|
|
|
struct base_data *child;
|
|
|
|
|
2020-10-07 18:19:23 +00:00
|
|
|
counter_lock();
|
|
|
|
display_progress(progress, nr_resolved_deltas);
|
|
|
|
counter_unlock();
|
|
|
|
|
2013-03-19 14:16:41 +00:00
|
|
|
work_lock();
|
index-pack: make quantum of work smaller
Currently, when index-pack resolves deltas, it does not split up delta
trees into threads: each delta base root (an object that is not a
REF_DELTA or OFS_DELTA) can go into its own thread, but all deltas on
that root (direct or indirect) are processed in the same thread.
This is a problem when a repository contains a large text file (thus,
delta-able) that is modified many times - delta resolution time during
fetching is dominated by processing the deltas corresponding to that
text file.
This patch contains a solution to that. When cloning using
git -c core.deltabasecachelimit=1g clone \
https://fuchsia.googlesource.com/third_party/vulkan-cts
on my laptop, clone time improved from 3m2s to 2m5s (using 3 threads,
which is the default).
The solution is to have a global work stack. This stack contains delta
bases (objects, whether appearing directly in the packfile or generated
by delta resolution, that themselves have delta children) that need to
be processed; whenever a thread needs work, it peeks at the top of the
stack and processes its next unprocessed child. If a thread finds the
stack empty, it will look for more delta base roots to push on the stack
instead.
The main weakness of having a global work stack is that more time is
spent in the mutex, but profiling has shown that most time is spent in
the resolution of the deltas themselves, so this shouldn't be an issue
in practice. In any case, experimentation (as described in the clone
command above) shows that this patch is a net improvement.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-09-08 19:48:35 +00:00
|
|
|
if (list_empty(&work_head)) {
|
|
|
|
/*
|
|
|
|
* Take an object from the object array.
|
|
|
|
*/
|
|
|
|
while (nr_dispatched < nr_objects &&
|
|
|
|
is_delta_type(objects[nr_dispatched].type))
|
|
|
|
nr_dispatched++;
|
|
|
|
if (nr_dispatched >= nr_objects) {
|
|
|
|
work_unlock();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
child_obj = &objects[nr_dispatched++];
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Peek at the top of the stack, and take a child from
|
|
|
|
* it.
|
|
|
|
*/
|
|
|
|
parent = list_first_entry(&work_head, struct base_data,
|
|
|
|
list);
|
|
|
|
|
|
|
|
if (parent->ref_first <= parent->ref_last) {
|
|
|
|
int offset = ref_deltas[parent->ref_first++].obj_no;
|
|
|
|
child_obj = objects + offset;
|
|
|
|
if (child_obj->real_type != OBJ_REF_DELTA)
|
|
|
|
die("REF_DELTA at offset %"PRIuMAX" already resolved (duplicate base %s?)",
|
|
|
|
(uintmax_t) child_obj->idx.offset,
|
|
|
|
oid_to_hex(&parent->obj->idx.oid));
|
|
|
|
child_obj->real_type = parent->obj->real_type;
|
|
|
|
} else {
|
|
|
|
child_obj = objects +
|
|
|
|
ofs_deltas[parent->ofs_first++].obj_no;
|
|
|
|
assert(child_obj->real_type == OBJ_OFS_DELTA);
|
|
|
|
child_obj->real_type = parent->obj->real_type;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parent->ref_first > parent->ref_last &&
|
|
|
|
parent->ofs_first > parent->ofs_last) {
|
|
|
|
/*
|
|
|
|
* This parent has run out of children, so move
|
|
|
|
* it to done_head.
|
|
|
|
*/
|
|
|
|
list_del(&parent->list);
|
|
|
|
list_add(&parent->list, &done_head);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ensure that the parent has data, since we will need
|
|
|
|
* it later.
|
|
|
|
*
|
|
|
|
* NEEDSWORK: If parent data needs to be reloaded, this
|
|
|
|
* prolongs the time that the current thread spends in
|
|
|
|
* the mutex. A mitigating factor is that parent data
|
|
|
|
* needs to be reloaded only if the delta base cache
|
|
|
|
* limit is exceeded, so in the typical case, this does
|
|
|
|
* not happen.
|
|
|
|
*/
|
|
|
|
get_base_data(parent);
|
|
|
|
parent->retain_data++;
|
2012-05-06 12:31:55 +00:00
|
|
|
}
|
|
|
|
work_unlock();
|
|
|
|
|
index-pack: make quantum of work smaller
Currently, when index-pack resolves deltas, it does not split up delta
trees into threads: each delta base root (an object that is not a
REF_DELTA or OFS_DELTA) can go into its own thread, but all deltas on
that root (direct or indirect) are processed in the same thread.
This is a problem when a repository contains a large text file (thus,
delta-able) that is modified many times - delta resolution time during
fetching is dominated by processing the deltas corresponding to that
text file.
This patch contains a solution to that. When cloning using
git -c core.deltabasecachelimit=1g clone \
https://fuchsia.googlesource.com/third_party/vulkan-cts
on my laptop, clone time improved from 3m2s to 2m5s (using 3 threads,
which is the default).
The solution is to have a global work stack. This stack contains delta
bases (objects, whether appearing directly in the packfile or generated
by delta resolution, that themselves have delta children) that need to
be processed; whenever a thread needs work, it peeks at the top of the
stack and processes its next unprocessed child. If a thread finds the
stack empty, it will look for more delta base roots to push on the stack
instead.
The main weakness of having a global work stack is that more time is
spent in the mutex, but profiling has shown that most time is spent in
the resolution of the deltas themselves, so this shouldn't be an issue
in practice. In any case, experimentation (as described in the clone
command above) shows that this patch is a net improvement.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-09-08 19:48:35 +00:00
|
|
|
if (parent) {
|
|
|
|
child = resolve_delta(child_obj, parent);
|
|
|
|
if (!child->children_remaining)
|
|
|
|
FREE_AND_NULL(child->data);
|
|
|
|
} else {
|
|
|
|
child = make_base(child_obj, NULL);
|
|
|
|
if (child->children_remaining) {
|
|
|
|
/*
|
|
|
|
* Since this child has its own delta children,
|
|
|
|
* we will need this data in the future.
|
|
|
|
* Inflate now so that future iterations will
|
|
|
|
* have access to this object's data while
|
|
|
|
* outside the work mutex.
|
|
|
|
*/
|
|
|
|
child->data = get_data_from_pack(child_obj);
|
|
|
|
child->size = child_obj->size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
work_lock();
|
|
|
|
if (parent)
|
|
|
|
parent->retain_data--;
|
|
|
|
if (child->data) {
|
|
|
|
/*
|
|
|
|
* This child has its own children, so add it to
|
|
|
|
* work_head.
|
|
|
|
*/
|
|
|
|
list_add(&child->list, &work_head);
|
|
|
|
base_cache_used += child->size;
|
|
|
|
prune_base_data(NULL);
|
index-pack: fix memory leaks
Fix various memory leaks in "git index-pack", due to how tightly
coupled this command is with the revision walking this doesn't make
any new tests pass.
But e.g. this now passes, and had several failures before, i.e. we
still have failures in tests 3, 5 etc., which are being skipped here.
./t5300-pack-object.sh --run=1-2,4,6-27,30-42
It is a bit odd that we'll free "opts.anomaly", since the "opts" is a
"struct pack_idx_option" declared in pack.h. In pack-write.c there's a
reset_pack_idx_option(), but it only wipes the contents, but doesn't
free() anything.
Doing this here in cmd_index_pack() is correct because while the
struct is declared in pack.h, this code in builtin/index-pack.c (in
read_v2_anomalous_offsets()) is what allocates the "opts.anomaly", so
we should also free it here.
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-03-04 18:32:04 +00:00
|
|
|
free_base_data(child);
|
index-pack: make quantum of work smaller
Currently, when index-pack resolves deltas, it does not split up delta
trees into threads: each delta base root (an object that is not a
REF_DELTA or OFS_DELTA) can go into its own thread, but all deltas on
that root (direct or indirect) are processed in the same thread.
This is a problem when a repository contains a large text file (thus,
delta-able) that is modified many times - delta resolution time during
fetching is dominated by processing the deltas corresponding to that
text file.
This patch contains a solution to that. When cloning using
git -c core.deltabasecachelimit=1g clone \
https://fuchsia.googlesource.com/third_party/vulkan-cts
on my laptop, clone time improved from 3m2s to 2m5s (using 3 threads,
which is the default).
The solution is to have a global work stack. This stack contains delta
bases (objects, whether appearing directly in the packfile or generated
by delta resolution, that themselves have delta children) that need to
be processed; whenever a thread needs work, it peeks at the top of the
stack and processes its next unprocessed child. If a thread finds the
stack empty, it will look for more delta base roots to push on the stack
instead.
The main weakness of having a global work stack is that more time is
spent in the mutex, but profiling has shown that most time is spent in
the resolution of the deltas themselves, so this shouldn't be an issue
in practice. In any case, experimentation (as described in the clone
command above) shows that this patch is a net improvement.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-09-08 19:48:35 +00:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* This child does not have its own children. It may be
|
|
|
|
* the last descendant of its ancestors; free those
|
|
|
|
* that we can.
|
|
|
|
*/
|
|
|
|
struct base_data *p = parent;
|
|
|
|
|
|
|
|
while (p) {
|
|
|
|
struct base_data *next_p;
|
|
|
|
|
|
|
|
p->children_remaining--;
|
|
|
|
if (p->children_remaining)
|
|
|
|
break;
|
|
|
|
|
|
|
|
next_p = p->base;
|
|
|
|
free_base_data(p);
|
|
|
|
list_del(&p->list);
|
|
|
|
free(p);
|
|
|
|
|
|
|
|
p = next_p;
|
|
|
|
}
|
index-pack: fix memory leaks
Fix various memory leaks in "git index-pack", due to how tightly
coupled this command is with the revision walking this doesn't make
any new tests pass.
But e.g. this now passes, and had several failures before, i.e. we
still have failures in tests 3, 5 etc., which are being skipped here.
./t5300-pack-object.sh --run=1-2,4,6-27,30-42
It is a bit odd that we'll free "opts.anomaly", since the "opts" is a
"struct pack_idx_option" declared in pack.h. In pack-write.c there's a
reset_pack_idx_option(), but it only wipes the contents, but doesn't
free() anything.
Doing this here in cmd_index_pack() is correct because while the
struct is declared in pack.h, this code in builtin/index-pack.c (in
read_v2_anomalous_offsets()) is what allocates the "opts.anomaly", so
we should also free it here.
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-03-04 18:32:04 +00:00
|
|
|
FREE_AND_NULL(child);
|
index-pack: make quantum of work smaller
Currently, when index-pack resolves deltas, it does not split up delta
trees into threads: each delta base root (an object that is not a
REF_DELTA or OFS_DELTA) can go into its own thread, but all deltas on
that root (direct or indirect) are processed in the same thread.
This is a problem when a repository contains a large text file (thus,
delta-able) that is modified many times - delta resolution time during
fetching is dominated by processing the deltas corresponding to that
text file.
This patch contains a solution to that. When cloning using
git -c core.deltabasecachelimit=1g clone \
https://fuchsia.googlesource.com/third_party/vulkan-cts
on my laptop, clone time improved from 3m2s to 2m5s (using 3 threads,
which is the default).
The solution is to have a global work stack. This stack contains delta
bases (objects, whether appearing directly in the packfile or generated
by delta resolution, that themselves have delta children) that need to
be processed; whenever a thread needs work, it peeks at the top of the
stack and processes its next unprocessed child. If a thread finds the
stack empty, it will look for more delta base roots to push on the stack
instead.
The main weakness of having a global work stack is that more time is
spent in the mutex, but profiling has shown that most time is spent in
the resolution of the deltas themselves, so this shouldn't be an issue
in practice. In any case, experimentation (as described in the clone
command above) shows that this patch is a net improvement.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-09-08 19:48:35 +00:00
|
|
|
}
|
|
|
|
work_unlock();
|
2012-05-06 12:31:55 +00:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-05-06 12:31:54 +00:00
|
|
|
/*
|
|
|
|
* First pass:
|
|
|
|
* - find locations of all objects;
|
|
|
|
* - calculate SHA1 of all non-delta objects;
|
|
|
|
* - remember base (SHA1 or offset) for all deltas.
|
|
|
|
*/
|
2018-02-01 02:18:39 +00:00
|
|
|
static void parse_pack_objects(unsigned char *hash)
|
2005-10-12 19:01:31 +00:00
|
|
|
{
|
2012-05-23 14:09:47 +00:00
|
|
|
int i, nr_delays = 0;
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
struct ofs_delta_entry *ofs_delta = ofs_deltas;
|
2018-02-01 02:18:39 +00:00
|
|
|
struct object_id ref_delta_oid;
|
2006-10-20 18:45:21 +00:00
|
|
|
struct stat st;
|
2023-09-01 02:09:28 +00:00
|
|
|
git_hash_ctx tmp_ctx;
|
2005-10-12 19:01:31 +00:00
|
|
|
|
2007-04-20 18:10:07 +00:00
|
|
|
if (verbose)
|
2007-10-30 18:57:35 +00:00
|
|
|
progress = start_progress(
|
index-pack: add --progress-title option
Add a --progress-title option to index-pack, when data is piped into
index-pack its progress is a proxy for whatever's feeding it data.
This option will allow us to set a more relevant progress bar title in
"git bundle unbundle", and is also used in my "bundle-uri" RFC
patches[1] by a new caller in fetch-pack.c.
The code change in cmd_index_pack() won't handle
"--progress-title=xyz", only "--progress-title xyz", and the "(i+1)"
style (as opposed to "i + 1") is a bit odd.
Not using the "--long-option=value" style is inconsistent with
existing long options handled by cmd_index_pack(), but makes the code
that needs to call it better (two strvec_push(), instead of needing a
strvec_pushf()). Since the option is internal-only the inconsistency
shouldn't matter.
I'm copying the pattern to handle it as-is from the handling of the
existing "-o" option in the same function, see 9cf6d3357aa (Add
git-index-pack utility, 2005-10-12) for its addition. That's a short
option, but the code to implement the two is the same in functionality
and style. Eventually we'd like to migrate all of this this to
parse_options(), which would make these differences in behavior go
away.
1. https://lore.kernel.org/git/RFC-cover-00.13-0000000000-20210805T150534Z-avarab@gmail.com/
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-09-05 07:34:44 +00:00
|
|
|
progress_title ? progress_title :
|
2012-04-23 12:30:29 +00:00
|
|
|
from_stdin ? _("Receiving objects") : _("Indexing objects"),
|
2007-10-30 18:57:35 +00:00
|
|
|
nr_objects);
|
2005-10-12 19:01:31 +00:00
|
|
|
for (i = 0; i < nr_objects; i++) {
|
|
|
|
struct object_entry *obj = &objects[i];
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
void *data = unpack_raw_entry(obj, &ofs_delta->offset,
|
2018-02-01 02:18:39 +00:00
|
|
|
&ref_delta_oid,
|
|
|
|
&obj->idx.oid);
|
2005-10-12 19:01:31 +00:00
|
|
|
obj->real_type = obj->type;
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
if (obj->type == OBJ_OFS_DELTA) {
|
|
|
|
nr_ofs_deltas++;
|
|
|
|
ofs_delta->obj_no = i;
|
|
|
|
ofs_delta++;
|
|
|
|
} else if (obj->type == OBJ_REF_DELTA) {
|
|
|
|
ALLOC_GROW(ref_deltas, nr_ref_deltas + 1, ref_deltas_alloc);
|
2018-03-12 02:27:37 +00:00
|
|
|
oidcpy(&ref_deltas[nr_ref_deltas].oid, &ref_delta_oid);
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
ref_deltas[nr_ref_deltas].obj_no = i;
|
|
|
|
nr_ref_deltas++;
|
2012-05-23 14:09:47 +00:00
|
|
|
} else if (!data) {
|
|
|
|
/* large blobs, check later */
|
|
|
|
obj->real_type = OBJ_BAD;
|
|
|
|
nr_delays++;
|
2005-10-12 19:01:31 +00:00
|
|
|
} else
|
2017-05-06 22:10:11 +00:00
|
|
|
sha1_object(data, NULL, obj->size, obj->type,
|
2017-05-06 22:10:13 +00:00
|
|
|
&obj->idx.oid);
|
2005-10-12 19:01:31 +00:00
|
|
|
free(data);
|
2007-10-30 18:57:33 +00:00
|
|
|
display_progress(progress, i+1);
|
2005-10-12 19:01:31 +00:00
|
|
|
}
|
2007-06-01 19:18:05 +00:00
|
|
|
objects[i].idx.offset = consumed_bytes;
|
2007-10-30 18:57:33 +00:00
|
|
|
stop_progress(&progress);
|
2006-10-20 18:45:21 +00:00
|
|
|
|
|
|
|
/* Check pack integrity */
|
2006-10-26 03:28:17 +00:00
|
|
|
flush();
|
2023-09-01 02:09:28 +00:00
|
|
|
the_hash_algo->init_fn(&tmp_ctx);
|
|
|
|
the_hash_algo->clone_fn(&tmp_ctx, &input_ctx);
|
|
|
|
the_hash_algo->final_fn(hash, &tmp_ctx);
|
2018-08-28 21:22:52 +00:00
|
|
|
if (!hasheq(fill(the_hash_algo->rawsz), hash))
|
2012-04-23 12:30:29 +00:00
|
|
|
die(_("pack is corrupted (SHA1 mismatch)"));
|
2018-02-01 02:18:39 +00:00
|
|
|
use(the_hash_algo->rawsz);
|
2006-10-20 18:45:21 +00:00
|
|
|
|
|
|
|
/* If input_fd is a file, we should have reached its end now. */
|
|
|
|
if (fstat(input_fd, &st))
|
2012-04-23 12:30:29 +00:00
|
|
|
die_errno(_("cannot fstat packfile"));
|
git-bundle: assorted fixes
This patch fixes issues mentioned by Junio, Nico and Simon:
- I forgot to convert the usage string when removing the "--" from
the subcommands,
- a style fix in the bundle_header,
- use xread() instead of read(),
- use write_or_die() instead of write(),
- make the bundle header extensible,
- fail if the whitespace after a sha1 of a reference is missing,
- close() the fds passed to a subprocess,
- in verify_bundle(), do not use "rev-list --stdin", but rather
pass the revs directly (avoiding a fork()),
- fix a corrupted comment in show_object(), and
- fix the size check in index_pack.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-02-22 18:14:14 +00:00
|
|
|
if (S_ISREG(st.st_mode) &&
|
|
|
|
lseek(input_fd, 0, SEEK_CUR) - input_len != st.st_size)
|
2012-04-23 12:30:29 +00:00
|
|
|
die(_("pack has junk at the end"));
|
2012-05-23 14:09:47 +00:00
|
|
|
|
|
|
|
for (i = 0; i < nr_objects; i++) {
|
|
|
|
struct object_entry *obj = &objects[i];
|
|
|
|
if (obj->real_type != OBJ_BAD)
|
|
|
|
continue;
|
|
|
|
obj->real_type = obj->type;
|
2017-05-06 22:10:11 +00:00
|
|
|
sha1_object(NULL, obj, obj->size, obj->type,
|
2017-05-06 22:10:13 +00:00
|
|
|
&obj->idx.oid);
|
2012-05-23 14:09:47 +00:00
|
|
|
nr_delays--;
|
|
|
|
}
|
|
|
|
if (nr_delays)
|
|
|
|
die(_("confusion beyond insanity in parse_pack_objects()"));
|
2012-05-06 12:31:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Second pass:
|
|
|
|
* - for all non-delta objects, look if it is used as a base for
|
|
|
|
* deltas;
|
|
|
|
* - if used as a base, uncompress the object and apply all deltas,
|
|
|
|
* recursively checking if the resulting object is used as a base
|
|
|
|
* for some more deltas.
|
|
|
|
*/
|
|
|
|
static void resolve_deltas(void)
|
|
|
|
{
|
|
|
|
int i;
|
2005-10-12 19:01:31 +00:00
|
|
|
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
if (!nr_ofs_deltas && !nr_ref_deltas)
|
2006-10-26 03:32:59 +00:00
|
|
|
return;
|
|
|
|
|
2006-09-21 04:08:33 +00:00
|
|
|
/* Sort deltas by base SHA1/offset for fast searching */
|
2016-09-29 15:27:31 +00:00
|
|
|
QSORT(ofs_deltas, nr_ofs_deltas, compare_ofs_delta_entry);
|
|
|
|
QSORT(ref_deltas, nr_ref_deltas, compare_ref_delta_entry);
|
2005-10-12 19:01:31 +00:00
|
|
|
|
index-pack: add flag for showing delta-resolution progress
The index-pack command has two progress meters: one for
"receiving objects", and one for "resolving deltas". You get
neither by default, or both with "-v".
But for a push through receive-pack, we would want only the
"resolving deltas" phase, _not_ the "receiving objects"
progress. There are two reasons for this.
One is simply that existing clients are already printing
"writing objects" progress at the same time. Arguably
"receiving" from the far end is more useful, because it
tells you what has actually gotten there, as opposed to what
might be stuck in a buffer somewhere between the client and
server. But that would require a protocol extension to tell
clients not to print their progress. Possible, but
complexity for little gain.
The second reason is much more important. In a full-duplex
connection like git-over-ssh, we can print progress while
the pack is incoming, and it will immediately get to the
client. But for a half-duplex connection like git-over-http,
we should not say anything until we have received the full
request. Anything we write is subject to being stuck in a
buffer by the webserver. Worse, we can end up in a deadlock
if that buffer fills up.
So our best bet is to avoid writing anything that isn't a
small fixed size until we've received the full pack.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-07-15 10:34:22 +00:00
|
|
|
if (verbose || show_resolving_progress)
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
progress = start_progress(_("Resolving deltas"),
|
|
|
|
nr_ref_deltas + nr_ofs_deltas);
|
2012-05-06 12:31:55 +00:00
|
|
|
|
|
|
|
nr_dispatched = 0;
|
index-pack: make quantum of work smaller
Currently, when index-pack resolves deltas, it does not split up delta
trees into threads: each delta base root (an object that is not a
REF_DELTA or OFS_DELTA) can go into its own thread, but all deltas on
that root (direct or indirect) are processed in the same thread.
This is a problem when a repository contains a large text file (thus,
delta-able) that is modified many times - delta resolution time during
fetching is dominated by processing the deltas corresponding to that
text file.
This patch contains a solution to that. When cloning using
git -c core.deltabasecachelimit=1g clone \
https://fuchsia.googlesource.com/third_party/vulkan-cts
on my laptop, clone time improved from 3m2s to 2m5s (using 3 threads,
which is the default).
The solution is to have a global work stack. This stack contains delta
bases (objects, whether appearing directly in the packfile or generated
by delta resolution, that themselves have delta children) that need to
be processed; whenever a thread needs work, it peeks at the top of the
stack and processes its next unprocessed child. If a thread finds the
stack empty, it will look for more delta base roots to push on the stack
instead.
The main weakness of having a global work stack is that more time is
spent in the mutex, but profiling has shown that most time is spent in
the resolution of the deltas themselves, so this shouldn't be an issue
in practice. In any case, experimentation (as described in the clone
command above) shows that this patch is a net improvement.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-09-08 19:48:35 +00:00
|
|
|
base_cache_limit = delta_base_cache_limit * nr_threads;
|
2012-05-06 12:31:55 +00:00
|
|
|
if (nr_threads > 1 || getenv("GIT_FORCE_THREADS")) {
|
|
|
|
init_thread();
|
index-pack: spawn threads atomically
The t5309 script triggers a racy false positive with SANITIZE=leak on a
multi-core system. Running with "--stress --run=6" usually fails within
10 seconds or so for me, complaining with something like:
+ git index-pack --fix-thin --stdin
fatal: REF_DELTA at offset 46 already resolved (duplicate base 01d7713666f4de822776c7622c10f1b07de280dc?)
=================================================================
==3904583==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 32 byte(s) in 1 object(s) allocated from:
#0 0x7fa790d01986 in __interceptor_realloc ../../../../src/libsanitizer/lsan/lsan_interceptors.cpp:98
#1 0x7fa790add769 in __pthread_getattr_np nptl/pthread_getattr_np.c:180
#2 0x7fa790d117c5 in __sanitizer::GetThreadStackTopAndBottom(bool, unsigned long*, unsigned long*) ../../../../src/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cpp:150
#3 0x7fa790d11957 in __sanitizer::GetThreadStackAndTls(bool, unsigned long*, unsigned long*, unsigned long*, unsigned long*) ../../../../src/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cpp:598
#4 0x7fa790d03fe8 in __lsan::ThreadStart(unsigned int, unsigned long long, __sanitizer::ThreadType) ../../../../src/libsanitizer/lsan/lsan_posix.cpp:51
#5 0x7fa790d013fd in __lsan_thread_start_func ../../../../src/libsanitizer/lsan/lsan_interceptors.cpp:440
#6 0x7fa790adc3eb in start_thread nptl/pthread_create.c:444
#7 0x7fa790b5ca5b in clone3 ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
SUMMARY: LeakSanitizer: 32 byte(s) leaked in 1 allocation(s).
Aborted
What happens is this:
0. We construct a bogus pack with a duplicate object in it and trigger
index-pack.
1. We spawn a bunch of worker threads to resolve deltas (on my system
it is 16 threads).
2. One of the threads sees the duplicate object and bails by calling
exit(), taking down all of the threads. This is expected and is the
point of the test.
3. At the time exit() is called, we may still be spawning threads from
the main process via pthread_create(). LSan hooks thread creation
to update its book-keeping; it has to know where each thread's
stack is (so it can find entry points for reachable memory). So it
calls pthread_getattr_np() to get information about the new thread.
That may allocate memory that must be freed with a matching call to
pthread_attr_destroy(). Probably LSan does that immediately, but
if you're unlucky enough, the exit() will happen while it's between
those two calls, and the allocated pthread_attr_t appears as a
leak.
This isn't a real leak. It's not even in our code, but rather in the
LSan instrumentation code. So we could just ignore it. But the false
positive can cause people to waste time tracking it down.
It's possibly something that LSan could protect against (e.g., cover the
getattr/destroy pair with a mutex, and then in the final post-exit()
check for leaks try to take the same mutex). But I don't know enough
about LSan to say if that's a reasonable approach or not (or if my
analysis is even completely correct).
In the meantime, it's pretty easy to avoid the race by making creation
of the worker threads "atomic". That is, we'll spawn all of them before
letting any of them start to work. That's easy to do because we already
have a work_lock() mutex for handing out that work. If the main process
takes it, then all of the threads will immediately block until we've
finished spawning and released it.
This shouldn't make any practical difference for non-LSan runs. The
thread spawning is quick, and could happen before any worker thread gets
scheduled anyway.
Probably other spots that use threads are subject to the same issues.
But since we have to manually insert locking (and since this really is
kind of a hack), let's not bother with them unless somebody experiences
a similar racy false-positive in practice.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-01-05 08:50:34 +00:00
|
|
|
work_lock();
|
2012-05-06 12:31:55 +00:00
|
|
|
for (i = 0; i < nr_threads; i++) {
|
|
|
|
int ret = pthread_create(&thread_data[i].thread, NULL,
|
|
|
|
threaded_second_pass, thread_data + i);
|
|
|
|
if (ret)
|
2012-08-31 12:13:04 +00:00
|
|
|
die(_("unable to create thread: %s"),
|
|
|
|
strerror(ret));
|
2012-05-06 12:31:55 +00:00
|
|
|
}
|
index-pack: spawn threads atomically
The t5309 script triggers a racy false positive with SANITIZE=leak on a
multi-core system. Running with "--stress --run=6" usually fails within
10 seconds or so for me, complaining with something like:
+ git index-pack --fix-thin --stdin
fatal: REF_DELTA at offset 46 already resolved (duplicate base 01d7713666f4de822776c7622c10f1b07de280dc?)
=================================================================
==3904583==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 32 byte(s) in 1 object(s) allocated from:
#0 0x7fa790d01986 in __interceptor_realloc ../../../../src/libsanitizer/lsan/lsan_interceptors.cpp:98
#1 0x7fa790add769 in __pthread_getattr_np nptl/pthread_getattr_np.c:180
#2 0x7fa790d117c5 in __sanitizer::GetThreadStackTopAndBottom(bool, unsigned long*, unsigned long*) ../../../../src/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cpp:150
#3 0x7fa790d11957 in __sanitizer::GetThreadStackAndTls(bool, unsigned long*, unsigned long*, unsigned long*, unsigned long*) ../../../../src/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cpp:598
#4 0x7fa790d03fe8 in __lsan::ThreadStart(unsigned int, unsigned long long, __sanitizer::ThreadType) ../../../../src/libsanitizer/lsan/lsan_posix.cpp:51
#5 0x7fa790d013fd in __lsan_thread_start_func ../../../../src/libsanitizer/lsan/lsan_interceptors.cpp:440
#6 0x7fa790adc3eb in start_thread nptl/pthread_create.c:444
#7 0x7fa790b5ca5b in clone3 ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
SUMMARY: LeakSanitizer: 32 byte(s) leaked in 1 allocation(s).
Aborted
What happens is this:
0. We construct a bogus pack with a duplicate object in it and trigger
index-pack.
1. We spawn a bunch of worker threads to resolve deltas (on my system
it is 16 threads).
2. One of the threads sees the duplicate object and bails by calling
exit(), taking down all of the threads. This is expected and is the
point of the test.
3. At the time exit() is called, we may still be spawning threads from
the main process via pthread_create(). LSan hooks thread creation
to update its book-keeping; it has to know where each thread's
stack is (so it can find entry points for reachable memory). So it
calls pthread_getattr_np() to get information about the new thread.
That may allocate memory that must be freed with a matching call to
pthread_attr_destroy(). Probably LSan does that immediately, but
if you're unlucky enough, the exit() will happen while it's between
those two calls, and the allocated pthread_attr_t appears as a
leak.
This isn't a real leak. It's not even in our code, but rather in the
LSan instrumentation code. So we could just ignore it. But the false
positive can cause people to waste time tracking it down.
It's possibly something that LSan could protect against (e.g., cover the
getattr/destroy pair with a mutex, and then in the final post-exit()
check for leaks try to take the same mutex). But I don't know enough
about LSan to say if that's a reasonable approach or not (or if my
analysis is even completely correct).
In the meantime, it's pretty easy to avoid the race by making creation
of the worker threads "atomic". That is, we'll spawn all of them before
letting any of them start to work. That's easy to do because we already
have a work_lock() mutex for handing out that work. If the main process
takes it, then all of the threads will immediately block until we've
finished spawning and released it.
This shouldn't make any practical difference for non-LSan runs. The
thread spawning is quick, and could happen before any worker thread gets
scheduled anyway.
Probably other spots that use threads are subject to the same issues.
But since we have to manually insert locking (and since this really is
kind of a hack), let's not bother with them unless somebody experiences
a similar racy false-positive in practice.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-01-05 08:50:34 +00:00
|
|
|
work_unlock();
|
2012-05-06 12:31:55 +00:00
|
|
|
for (i = 0; i < nr_threads; i++)
|
|
|
|
pthread_join(thread_data[i].thread, NULL);
|
|
|
|
cleanup_thread();
|
|
|
|
return;
|
|
|
|
}
|
2020-08-24 19:16:34 +00:00
|
|
|
threaded_second_pass(¬hread_data);
|
2006-10-26 03:28:17 +00:00
|
|
|
}
|
|
|
|
|
2012-05-06 12:31:54 +00:00
|
|
|
/*
|
|
|
|
* Third pass:
|
|
|
|
* - append objects to convert thin pack to full pack if required
|
2018-02-01 02:18:39 +00:00
|
|
|
* - write the final pack hash
|
2012-05-06 12:31:54 +00:00
|
|
|
*/
|
2018-02-01 02:18:46 +00:00
|
|
|
static void fix_unresolved_deltas(struct hashfile *f);
|
2018-02-01 02:18:39 +00:00
|
|
|
static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned char *pack_hash)
|
2012-05-06 12:31:54 +00:00
|
|
|
{
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
if (nr_ref_deltas + nr_ofs_deltas == nr_resolved_deltas) {
|
2012-05-06 12:31:54 +00:00
|
|
|
stop_progress(&progress);
|
2018-02-01 02:18:39 +00:00
|
|
|
/* Flush remaining pack final hash. */
|
2012-05-06 12:31:54 +00:00
|
|
|
flush();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fix_thin_pack) {
|
2018-02-01 02:18:46 +00:00
|
|
|
struct hashfile *f;
|
2018-02-01 02:18:39 +00:00
|
|
|
unsigned char read_hash[GIT_MAX_RAWSZ], tail_hash[GIT_MAX_RAWSZ];
|
2013-03-16 01:25:18 +00:00
|
|
|
struct strbuf msg = STRBUF_INIT;
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
int nr_unresolved = nr_ofs_deltas + nr_ref_deltas - nr_resolved_deltas;
|
2012-05-06 12:31:54 +00:00
|
|
|
int nr_objects_initial = nr_objects;
|
|
|
|
if (nr_unresolved <= 0)
|
2012-05-14 18:50:40 +00:00
|
|
|
die(_("confusion beyond insanity"));
|
2014-09-16 18:56:57 +00:00
|
|
|
REALLOC_ARRAY(objects, nr_objects + nr_unresolved + 1);
|
2013-03-19 16:17:22 +00:00
|
|
|
memset(objects + nr_objects + 1, 0,
|
|
|
|
nr_unresolved * sizeof(*objects));
|
2018-02-01 02:18:46 +00:00
|
|
|
f = hashfd(output_fd, curr_pack);
|
index-pack: fix allocation of sorted_by_pos array
When c6458e60 (index-pack: kill union delta_base to save memory,
2015-04-18) attempted to reduce the memory footprint of index-pack,
one of the key thing it did was to keep track of ref-deltas and
ofs-deltas separately.
In fix_unresolved_deltas(), however it forgot that it now wants to
look only at ref deltas in one place. The code allocated an array
for nr_unresolved, which is sum of number of ref- and ofs-deltas
minus nr_resolved, which may be larger or smaller than the number
ref-deltas. Depending on nr_resolved, this was either under or over
allocating.
Also, the old code before this change had to use 'i' and 'n' because
some of the things we see in the (old) deltas[] array we scanned
with 'i' would not make it into the sorted_by_pos[] array in the old
world order, but now because you have only ref delta in a separate
ref_deltas[] array, they increment lock&step. We no longer need
separate variables. And most importantly, we shouldn't pass the
nr_unresolved parameter, as this number does not play a role in the
working of this helper function.
Helped-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-07-03 16:51:57 +00:00
|
|
|
fix_unresolved_deltas(f);
|
2016-04-08 20:02:39 +00:00
|
|
|
strbuf_addf(&msg, Q_("completed with %d local object",
|
|
|
|
"completed with %d local objects",
|
|
|
|
nr_objects - nr_objects_initial),
|
2013-03-16 01:25:18 +00:00
|
|
|
nr_objects - nr_objects_initial);
|
|
|
|
stop_progress_msg(&progress, msg.buf);
|
|
|
|
strbuf_release(&msg);
|
2022-03-10 22:43:21 +00:00
|
|
|
finalize_hashfile(f, tail_hash, FSYNC_COMPONENT_PACK, 0);
|
2018-02-01 02:18:39 +00:00
|
|
|
hashcpy(read_hash, pack_hash);
|
|
|
|
fixup_pack_header_footer(output_fd, pack_hash,
|
2012-05-06 12:31:54 +00:00
|
|
|
curr_pack, nr_objects,
|
2018-02-01 02:18:39 +00:00
|
|
|
read_hash, consumed_bytes-the_hash_algo->rawsz);
|
2018-08-28 21:22:52 +00:00
|
|
|
if (!hasheq(read_hash, tail_hash))
|
2012-08-31 12:13:04 +00:00
|
|
|
die(_("Unexpected tail checksum for %s "
|
|
|
|
"(disk corruption?)"), curr_pack);
|
2012-05-06 12:31:54 +00:00
|
|
|
}
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
if (nr_ofs_deltas + nr_ref_deltas != nr_resolved_deltas)
|
2012-05-14 18:50:40 +00:00
|
|
|
die(Q_("pack has %d unresolved delta",
|
|
|
|
"pack has %d unresolved deltas",
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
nr_ofs_deltas + nr_ref_deltas - nr_resolved_deltas),
|
|
|
|
nr_ofs_deltas + nr_ref_deltas - nr_resolved_deltas);
|
2012-05-06 12:31:54 +00:00
|
|
|
}
|
|
|
|
|
2018-02-01 02:18:46 +00:00
|
|
|
static int write_compressed(struct hashfile *f, void *in, unsigned int size)
|
2006-10-26 03:28:17 +00:00
|
|
|
{
|
2011-06-10 18:52:15 +00:00
|
|
|
git_zstream stream;
|
2010-04-12 20:50:35 +00:00
|
|
|
int status;
|
|
|
|
unsigned char outbuf[4096];
|
2006-10-26 03:28:17 +00:00
|
|
|
|
2011-06-10 17:55:10 +00:00
|
|
|
git_deflate_init(&stream, zlib_compression_level);
|
2006-10-26 03:28:17 +00:00
|
|
|
stream.next_in = in;
|
|
|
|
stream.avail_in = size;
|
|
|
|
|
2010-04-12 20:50:35 +00:00
|
|
|
do {
|
|
|
|
stream.next_out = outbuf;
|
|
|
|
stream.avail_out = sizeof(outbuf);
|
2011-06-10 17:55:10 +00:00
|
|
|
status = git_deflate(&stream, Z_FINISH);
|
2018-02-01 02:18:46 +00:00
|
|
|
hashwrite(f, outbuf, sizeof(outbuf) - stream.avail_out);
|
2010-04-12 20:50:35 +00:00
|
|
|
} while (status == Z_OK);
|
|
|
|
|
|
|
|
if (status != Z_STREAM_END)
|
2012-04-23 12:30:29 +00:00
|
|
|
die(_("unable to deflate appended object (%d)"), status);
|
2006-10-26 03:28:17 +00:00
|
|
|
size = stream.total_out;
|
2011-06-10 17:55:10 +00:00
|
|
|
git_deflate_end(&stream);
|
2006-10-26 03:28:17 +00:00
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2018-02-01 02:18:46 +00:00
|
|
|
static struct object_entry *append_obj_to_pack(struct hashfile *f,
|
2008-07-14 02:07:46 +00:00
|
|
|
const unsigned char *sha1, void *buf,
|
2006-10-26 03:28:17 +00:00
|
|
|
unsigned long size, enum object_type type)
|
|
|
|
{
|
|
|
|
struct object_entry *obj = &objects[nr_objects++];
|
|
|
|
unsigned char header[10];
|
|
|
|
unsigned long s = size;
|
|
|
|
int n = 0;
|
|
|
|
unsigned char c = (type << 4) | (s & 15);
|
|
|
|
s >>= 4;
|
|
|
|
while (s) {
|
|
|
|
header[n++] = c | 0x80;
|
|
|
|
c = s & 0x7f;
|
|
|
|
s >>= 7;
|
|
|
|
}
|
|
|
|
header[n++] = c;
|
2008-08-29 20:08:01 +00:00
|
|
|
crc32_begin(f);
|
2018-02-01 02:18:46 +00:00
|
|
|
hashwrite(f, header, n);
|
2008-07-24 17:32:00 +00:00
|
|
|
obj[0].size = size;
|
|
|
|
obj[0].hdr_size = n;
|
|
|
|
obj[0].type = type;
|
|
|
|
obj[0].real_type = type;
|
2007-06-01 19:18:05 +00:00
|
|
|
obj[1].idx.offset = obj[0].idx.offset + n;
|
2008-08-29 20:08:01 +00:00
|
|
|
obj[1].idx.offset += write_compressed(f, buf, size);
|
|
|
|
obj[0].idx.crc32 = crc32_end(f);
|
2018-02-01 02:18:46 +00:00
|
|
|
hashflush(f);
|
2021-04-26 01:02:50 +00:00
|
|
|
oidread(&obj->idx.oid, sha1);
|
2008-07-14 02:07:46 +00:00
|
|
|
return obj;
|
2006-10-26 03:28:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int delta_pos_compare(const void *_a, const void *_b)
|
|
|
|
{
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
struct ref_delta_entry *a = *(struct ref_delta_entry **)_a;
|
|
|
|
struct ref_delta_entry *b = *(struct ref_delta_entry **)_b;
|
2006-10-26 03:28:17 +00:00
|
|
|
return a->obj_no - b->obj_no;
|
|
|
|
}
|
2005-10-12 19:01:31 +00:00
|
|
|
|
2018-02-01 02:18:46 +00:00
|
|
|
static void fix_unresolved_deltas(struct hashfile *f)
|
2006-10-26 03:28:17 +00:00
|
|
|
{
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
struct ref_delta_entry **sorted_by_pos;
|
index-pack: fix allocation of sorted_by_pos array
When c6458e60 (index-pack: kill union delta_base to save memory,
2015-04-18) attempted to reduce the memory footprint of index-pack,
one of the key thing it did was to keep track of ref-deltas and
ofs-deltas separately.
In fix_unresolved_deltas(), however it forgot that it now wants to
look only at ref deltas in one place. The code allocated an array
for nr_unresolved, which is sum of number of ref- and ofs-deltas
minus nr_resolved, which may be larger or smaller than the number
ref-deltas. Depending on nr_resolved, this was either under or over
allocating.
Also, the old code before this change had to use 'i' and 'n' because
some of the things we see in the (old) deltas[] array we scanned
with 'i' would not make it into the sorted_by_pos[] array in the old
world order, but now because you have only ref delta in a separate
ref_deltas[] array, they increment lock&step. We no longer need
separate variables. And most importantly, we shouldn't pass the
nr_unresolved parameter, as this number does not play a role in the
working of this helper function.
Helped-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-07-03 16:51:57 +00:00
|
|
|
int i;
|
2006-10-26 03:28:17 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Since many unresolved deltas may well be themselves base objects
|
|
|
|
* for more unresolved deltas, we really want to include the
|
|
|
|
* smallest number of base objects that would cover as much delta
|
|
|
|
* as possible by picking the
|
|
|
|
* trunc deltas first, allowing for other deltas to resolve without
|
|
|
|
* additional base objects. Since most base objects are to be found
|
|
|
|
* before deltas depending on them, a good heuristic is to start
|
|
|
|
* resolving deltas in the same order as their position in the pack.
|
|
|
|
*/
|
2016-02-22 22:44:25 +00:00
|
|
|
ALLOC_ARRAY(sorted_by_pos, nr_ref_deltas);
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
for (i = 0; i < nr_ref_deltas; i++)
|
index-pack: fix allocation of sorted_by_pos array
When c6458e60 (index-pack: kill union delta_base to save memory,
2015-04-18) attempted to reduce the memory footprint of index-pack,
one of the key thing it did was to keep track of ref-deltas and
ofs-deltas separately.
In fix_unresolved_deltas(), however it forgot that it now wants to
look only at ref deltas in one place. The code allocated an array
for nr_unresolved, which is sum of number of ref- and ofs-deltas
minus nr_resolved, which may be larger or smaller than the number
ref-deltas. Depending on nr_resolved, this was either under or over
allocating.
Also, the old code before this change had to use 'i' and 'n' because
some of the things we see in the (old) deltas[] array we scanned
with 'i' would not make it into the sorted_by_pos[] array in the old
world order, but now because you have only ref delta in a separate
ref_deltas[] array, they increment lock&step. We no longer need
separate variables. And most importantly, we shouldn't pass the
nr_unresolved parameter, as this number does not play a role in the
working of this helper function.
Helped-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-07-03 16:51:57 +00:00
|
|
|
sorted_by_pos[i] = &ref_deltas[i];
|
2016-09-29 15:27:31 +00:00
|
|
|
QSORT(sorted_by_pos, nr_ref_deltas, delta_pos_compare);
|
2006-10-26 03:28:17 +00:00
|
|
|
|
2023-03-28 13:58:53 +00:00
|
|
|
if (repo_has_promisor_remote(the_repository)) {
|
index-pack: prefetch missing REF_DELTA bases
When fetching, the client sends "have" commit IDs indicating that the
server does not need to send any object referenced by those commits,
reducing network I/O. When the client is a partial clone, the client
still sends "have"s in this way, even if it does not have every object
referenced by a commit it sent as "have".
If a server omits such an object, it is fine: the client could lazily
fetch that object before this fetch, and it can still do so after.
The issue is when the server sends a thin pack containing an object that
is a REF_DELTA against such a missing object: index-pack fails to fix
the thin pack. When support for lazily fetching missing objects was
added in 8b4c0103a9 ("sha1_file: support lazily fetching missing
objects", 2017-12-08), support in index-pack was turned off in the
belief that it accesses the repo only to do hash collision checks.
However, this is not true: it also needs to access the repo to resolve
REF_DELTA bases.
Support for lazy fetching should still generally be turned off in
index-pack because it is used as part of the lazy fetching process
itself (if not, infinite loops may occur), but we do need to fetch the
REF_DELTA bases. (When fetching REF_DELTA bases, it is unlikely that
those are REF_DELTA themselves, because we do not send "have" when
making such fetches.)
To resolve this, prefetch all missing REF_DELTA bases before attempting
to resolve them. This both ensures that all bases are attempted to be
fetched, and ensures that we make only one request per index-pack
invocation, and not one request per missing object.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-05-14 21:10:55 +00:00
|
|
|
/*
|
|
|
|
* Prefetch the delta bases.
|
|
|
|
*/
|
|
|
|
struct oid_array to_fetch = OID_ARRAY_INIT;
|
|
|
|
for (i = 0; i < nr_ref_deltas; i++) {
|
|
|
|
struct ref_delta_entry *d = sorted_by_pos[i];
|
|
|
|
if (!oid_object_info_extended(the_repository, &d->oid,
|
|
|
|
NULL,
|
|
|
|
OBJECT_INFO_FOR_PREFETCH))
|
|
|
|
continue;
|
|
|
|
oid_array_append(&to_fetch, &d->oid);
|
|
|
|
}
|
2020-04-02 19:19:16 +00:00
|
|
|
promisor_remote_get_direct(the_repository,
|
|
|
|
to_fetch.oid, to_fetch.nr);
|
index-pack: prefetch missing REF_DELTA bases
When fetching, the client sends "have" commit IDs indicating that the
server does not need to send any object referenced by those commits,
reducing network I/O. When the client is a partial clone, the client
still sends "have"s in this way, even if it does not have every object
referenced by a commit it sent as "have".
If a server omits such an object, it is fine: the client could lazily
fetch that object before this fetch, and it can still do so after.
The issue is when the server sends a thin pack containing an object that
is a REF_DELTA against such a missing object: index-pack fails to fix
the thin pack. When support for lazily fetching missing objects was
added in 8b4c0103a9 ("sha1_file: support lazily fetching missing
objects", 2017-12-08), support in index-pack was turned off in the
belief that it accesses the repo only to do hash collision checks.
However, this is not true: it also needs to access the repo to resolve
REF_DELTA bases.
Support for lazy fetching should still generally be turned off in
index-pack because it is used as part of the lazy fetching process
itself (if not, infinite loops may occur), but we do need to fetch the
REF_DELTA bases. (When fetching REF_DELTA bases, it is unlikely that
those are REF_DELTA themselves, because we do not send "have" when
making such fetches.)
To resolve this, prefetch all missing REF_DELTA bases before attempting
to resolve them. This both ensures that all bases are attempted to be
fetched, and ensures that we make only one request per index-pack
invocation, and not one request per missing object.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-05-14 21:10:55 +00:00
|
|
|
oid_array_clear(&to_fetch);
|
|
|
|
}
|
|
|
|
|
index-pack: fix allocation of sorted_by_pos array
When c6458e60 (index-pack: kill union delta_base to save memory,
2015-04-18) attempted to reduce the memory footprint of index-pack,
one of the key thing it did was to keep track of ref-deltas and
ofs-deltas separately.
In fix_unresolved_deltas(), however it forgot that it now wants to
look only at ref deltas in one place. The code allocated an array
for nr_unresolved, which is sum of number of ref- and ofs-deltas
minus nr_resolved, which may be larger or smaller than the number
ref-deltas. Depending on nr_resolved, this was either under or over
allocating.
Also, the old code before this change had to use 'i' and 'n' because
some of the things we see in the (old) deltas[] array we scanned
with 'i' would not make it into the sorted_by_pos[] array in the old
world order, but now because you have only ref delta in a separate
ref_deltas[] array, they increment lock&step. We no longer need
separate variables. And most importantly, we shouldn't pass the
nr_unresolved parameter, as this number does not play a role in the
working of this helper function.
Helped-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-07-03 16:51:57 +00:00
|
|
|
for (i = 0; i < nr_ref_deltas; i++) {
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
struct ref_delta_entry *d = sorted_by_pos[i];
|
2007-02-26 19:55:59 +00:00
|
|
|
enum object_type type;
|
2020-08-24 19:16:36 +00:00
|
|
|
void *data;
|
|
|
|
unsigned long size;
|
2006-10-26 03:28:17 +00:00
|
|
|
|
|
|
|
if (objects[d->obj_no].real_type != OBJ_REF_DELTA)
|
|
|
|
continue;
|
2023-03-28 13:58:50 +00:00
|
|
|
data = repo_read_object_file(the_repository, &d->oid, &type,
|
|
|
|
&size);
|
2020-08-24 19:16:36 +00:00
|
|
|
if (!data)
|
2006-10-26 03:28:17 +00:00
|
|
|
continue;
|
|
|
|
|
2022-02-04 23:48:29 +00:00
|
|
|
if (check_object_signature(the_repository, &d->oid, data, size,
|
2022-02-04 23:48:32 +00:00
|
|
|
type) < 0)
|
2018-03-12 02:27:37 +00:00
|
|
|
die(_("local object %s is corrupt"), oid_to_hex(&d->oid));
|
index-pack: make quantum of work smaller
Currently, when index-pack resolves deltas, it does not split up delta
trees into threads: each delta base root (an object that is not a
REF_DELTA or OFS_DELTA) can go into its own thread, but all deltas on
that root (direct or indirect) are processed in the same thread.
This is a problem when a repository contains a large text file (thus,
delta-able) that is modified many times - delta resolution time during
fetching is dominated by processing the deltas corresponding to that
text file.
This patch contains a solution to that. When cloning using
git -c core.deltabasecachelimit=1g clone \
https://fuchsia.googlesource.com/third_party/vulkan-cts
on my laptop, clone time improved from 3m2s to 2m5s (using 3 threads,
which is the default).
The solution is to have a global work stack. This stack contains delta
bases (objects, whether appearing directly in the packfile or generated
by delta resolution, that themselves have delta children) that need to
be processed; whenever a thread needs work, it peeks at the top of the
stack and processes its next unprocessed child. If a thread finds the
stack empty, it will look for more delta base roots to push on the stack
instead.
The main weakness of having a global work stack is that more time is
spent in the mutex, but profiling has shown that most time is spent in
the resolution of the deltas themselves, so this shouldn't be an issue
in practice. In any case, experimentation (as described in the clone
command above) shows that this patch is a net improvement.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-09-08 19:48:35 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Add this as an object to the objects array and call
|
|
|
|
* threaded_second_pass() (which will pick up the added
|
|
|
|
* object).
|
|
|
|
*/
|
|
|
|
append_obj_to_pack(f, d->oid.hash, data, size, type);
|
index-pack: fix memory leaks
Fix various memory leaks in "git index-pack", due to how tightly
coupled this command is with the revision walking this doesn't make
any new tests pass.
But e.g. this now passes, and had several failures before, i.e. we
still have failures in tests 3, 5 etc., which are being skipped here.
./t5300-pack-object.sh --run=1-2,4,6-27,30-42
It is a bit odd that we'll free "opts.anomaly", since the "opts" is a
"struct pack_idx_option" declared in pack.h. In pack-write.c there's a
reset_pack_idx_option(), but it only wipes the contents, but doesn't
free() anything.
Doing this here in cmd_index_pack() is correct because while the
struct is declared in pack.h, this code in builtin/index-pack.c (in
read_v2_anomalous_offsets()) is what allocates the "opts.anomaly", so
we should also free it here.
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-03-04 18:32:04 +00:00
|
|
|
free(data);
|
index-pack: make quantum of work smaller
Currently, when index-pack resolves deltas, it does not split up delta
trees into threads: each delta base root (an object that is not a
REF_DELTA or OFS_DELTA) can go into its own thread, but all deltas on
that root (direct or indirect) are processed in the same thread.
This is a problem when a repository contains a large text file (thus,
delta-able) that is modified many times - delta resolution time during
fetching is dominated by processing the deltas corresponding to that
text file.
This patch contains a solution to that. When cloning using
git -c core.deltabasecachelimit=1g clone \
https://fuchsia.googlesource.com/third_party/vulkan-cts
on my laptop, clone time improved from 3m2s to 2m5s (using 3 threads,
which is the default).
The solution is to have a global work stack. This stack contains delta
bases (objects, whether appearing directly in the packfile or generated
by delta resolution, that themselves have delta children) that need to
be processed; whenever a thread needs work, it peeks at the top of the
stack and processes its next unprocessed child. If a thread finds the
stack empty, it will look for more delta base roots to push on the stack
instead.
The main weakness of having a global work stack is that more time is
spent in the mutex, but profiling has shown that most time is spent in
the resolution of the deltas themselves, so this shouldn't be an issue
in practice. In any case, experimentation (as described in the clone
command above) shows that this patch is a net improvement.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-09-08 19:48:35 +00:00
|
|
|
threaded_second_pass(NULL);
|
|
|
|
|
2007-10-30 18:57:33 +00:00
|
|
|
display_progress(progress, nr_resolved_deltas);
|
2006-10-26 03:28:17 +00:00
|
|
|
}
|
|
|
|
free(sorted_by_pos);
|
|
|
|
}
|
|
|
|
|
2021-01-25 23:37:22 +00:00
|
|
|
static const char *derive_filename(const char *pack_name, const char *strip,
|
|
|
|
const char *suffix, struct strbuf *buf)
|
2017-12-05 16:58:48 +00:00
|
|
|
{
|
|
|
|
size_t len;
|
2021-01-25 23:37:22 +00:00
|
|
|
if (!strip_suffix(pack_name, strip, &len) || !len ||
|
|
|
|
pack_name[len - 1] != '.')
|
|
|
|
die(_("packfile name '%s' does not end with '.%s'"),
|
|
|
|
pack_name, strip);
|
2017-12-05 16:58:48 +00:00
|
|
|
strbuf_add(buf, pack_name, len);
|
|
|
|
strbuf_addstr(buf, suffix);
|
|
|
|
return buf->buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void write_special_file(const char *suffix, const char *msg,
|
2018-02-15 22:55:47 +00:00
|
|
|
const char *pack_name, const unsigned char *hash,
|
2017-12-05 16:58:48 +00:00
|
|
|
const char **report)
|
|
|
|
{
|
|
|
|
struct strbuf name_buf = STRBUF_INIT;
|
|
|
|
const char *filename;
|
|
|
|
int fd;
|
|
|
|
int msg_len = strlen(msg);
|
|
|
|
|
|
|
|
if (pack_name)
|
2021-01-25 23:37:22 +00:00
|
|
|
filename = derive_filename(pack_name, "pack", suffix, &name_buf);
|
2017-12-05 16:58:48 +00:00
|
|
|
else
|
2018-02-15 22:55:47 +00:00
|
|
|
filename = odb_pack_name(&name_buf, hash, suffix);
|
2017-12-05 16:58:48 +00:00
|
|
|
|
|
|
|
fd = odb_pack_keep(filename);
|
|
|
|
if (fd < 0) {
|
|
|
|
if (errno != EEXIST)
|
|
|
|
die_errno(_("cannot write %s file '%s'"),
|
|
|
|
suffix, filename);
|
|
|
|
} else {
|
|
|
|
if (msg_len > 0) {
|
|
|
|
write_or_die(fd, msg, msg_len);
|
|
|
|
write_or_die(fd, "\n", 1);
|
|
|
|
}
|
|
|
|
if (close(fd) != 0)
|
|
|
|
die_errno(_("cannot close written %s file '%s'"),
|
|
|
|
suffix, filename);
|
introduce fetch-object: fetch one promisor object
Introduce fetch-object, providing the ability to fetch one object from a
promisor remote.
This uses fetch-pack. To do this, the transport mechanism has been
updated with 2 flags, "from-promisor" to indicate that the resulting
pack comes from a promisor remote (and thus should be annotated as such
by index-pack), and "no-dependents" to indicate that only the objects
themselves need to be fetched (but fetching additional objects is
nevertheless safe).
Whenever "no-dependents" is used, fetch-pack will refrain from using any
object flags, because it is most likely invoked as part of a dynamic
object fetch by another Git command (which may itself use object flags).
An alternative to this is to leave fetch-pack alone, and instead update
the allocation of flags so that fetch-pack's flags never overlap with
any others, but this will end up shrinking the number of flags available
to nearly every other Git command (that is, every Git command that
accesses objects), so the approach in this commit was used instead.
This will be tested in a subsequent commit.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-12-05 16:58:49 +00:00
|
|
|
if (report)
|
|
|
|
*report = suffix;
|
2017-12-05 16:58:48 +00:00
|
|
|
}
|
|
|
|
strbuf_release(&name_buf);
|
|
|
|
}
|
|
|
|
|
2021-09-09 23:24:49 +00:00
|
|
|
static void rename_tmp_packfile(const char **final_name,
|
|
|
|
const char *curr_name,
|
|
|
|
struct strbuf *name, unsigned char *hash,
|
|
|
|
const char *ext, int make_read_only_if_same)
|
|
|
|
{
|
|
|
|
if (*final_name != curr_name) {
|
|
|
|
if (!*final_name)
|
|
|
|
*final_name = odb_pack_name(name, hash, ext);
|
|
|
|
if (finalize_object_file(curr_name, *final_name))
|
2021-11-01 02:14:17 +00:00
|
|
|
die(_("unable to rename temporary '*.%s' file to '%s'"),
|
2021-09-09 23:24:49 +00:00
|
|
|
ext, *final_name);
|
|
|
|
} else if (make_read_only_if_same) {
|
|
|
|
chmod(*final_name, 0444);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-10-23 18:50:18 +00:00
|
|
|
static void final(const char *final_pack_name, const char *curr_pack_name,
|
|
|
|
const char *final_index_name, const char *curr_index_name,
|
2021-01-25 23:37:26 +00:00
|
|
|
const char *final_rev_index_name, const char *curr_rev_index_name,
|
introduce fetch-object: fetch one promisor object
Introduce fetch-object, providing the ability to fetch one object from a
promisor remote.
This uses fetch-pack. To do this, the transport mechanism has been
updated with 2 flags, "from-promisor" to indicate that the resulting
pack comes from a promisor remote (and thus should be annotated as such
by index-pack), and "no-dependents" to indicate that only the objects
themselves need to be fetched (but fetching additional objects is
nevertheless safe).
Whenever "no-dependents" is used, fetch-pack will refrain from using any
object flags, because it is most likely invoked as part of a dynamic
object fetch by another Git command (which may itself use object flags).
An alternative to this is to leave fetch-pack alone, and instead update
the allocation of flags so that fetch-pack's flags never overlap with
any others, but this will end up shrinking the number of flags available
to nearly every other Git command (that is, every Git command that
accesses objects), so the approach in this commit was used instead.
This will be tested in a subsequent commit.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-12-05 16:58:49 +00:00
|
|
|
const char *keep_msg, const char *promisor_msg,
|
2018-02-01 02:18:39 +00:00
|
|
|
unsigned char *hash)
|
2006-10-23 18:50:18 +00:00
|
|
|
{
|
2007-03-07 01:44:17 +00:00
|
|
|
const char *report = "pack";
|
index-pack: make pointer-alias fallbacks safer
The final() function accepts a NULL value for certain
parameters, and falls back to writing into a reusable "name"
buffer, and then either:
1. For "keep_name", requiring all uses to do "keep_name ?
keep_name : name.buf". This is awkward, and it's easy
to accidentally look at the maybe-NULL keep_name.
2. For "final_index_name" and "final_pack_name", aliasing
those pointers to the "name" buffer. This is easier to
use, but the aliased pointers become invalid after the
buffer is reused (this isn't a bug now, but it's a
potential pitfall).
One way to make this safer would be to introduce an extra
pointer to do the aliasing, and have its lifetime match the
validity of the "name" buffer. But it's still easy to
accidentally use the wrong name (i.e., to use
"final_pack_name" instead of the aliased pointer).
Instead, let's use three separate buffers that will remain
valid through the function. That makes it safe to alias the
pointers and use them consistently. The extra allocations
shouldn't matter, as this function is not performance
sensitive.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-16 14:27:20 +00:00
|
|
|
struct strbuf pack_name = STRBUF_INIT;
|
|
|
|
struct strbuf index_name = STRBUF_INIT;
|
2021-01-25 23:37:26 +00:00
|
|
|
struct strbuf rev_index_name = STRBUF_INIT;
|
2006-10-23 18:50:18 +00:00
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!from_stdin) {
|
|
|
|
close(input_fd);
|
|
|
|
} else {
|
2022-03-10 22:43:21 +00:00
|
|
|
fsync_component_or_die(FSYNC_COMPONENT_PACK, output_fd, curr_pack_name);
|
2006-10-23 18:50:18 +00:00
|
|
|
err = close(output_fd);
|
|
|
|
if (err)
|
2012-04-23 12:30:29 +00:00
|
|
|
die_errno(_("error while closing pack file"));
|
2006-10-23 18:50:18 +00:00
|
|
|
}
|
|
|
|
|
2017-12-05 16:58:48 +00:00
|
|
|
if (keep_msg)
|
2018-02-15 22:55:47 +00:00
|
|
|
write_special_file("keep", keep_msg, final_pack_name, hash,
|
2017-12-05 16:58:48 +00:00
|
|
|
&report);
|
introduce fetch-object: fetch one promisor object
Introduce fetch-object, providing the ability to fetch one object from a
promisor remote.
This uses fetch-pack. To do this, the transport mechanism has been
updated with 2 flags, "from-promisor" to indicate that the resulting
pack comes from a promisor remote (and thus should be annotated as such
by index-pack), and "no-dependents" to indicate that only the objects
themselves need to be fetched (but fetching additional objects is
nevertheless safe).
Whenever "no-dependents" is used, fetch-pack will refrain from using any
object flags, because it is most likely invoked as part of a dynamic
object fetch by another Git command (which may itself use object flags).
An alternative to this is to leave fetch-pack alone, and instead update
the allocation of flags so that fetch-pack's flags never overlap with
any others, but this will end up shrinking the number of flags available
to nearly every other Git command (that is, every Git command that
accesses objects), so the approach in this commit was used instead.
This will be tested in a subsequent commit.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-12-05 16:58:49 +00:00
|
|
|
if (promisor_msg)
|
|
|
|
write_special_file("promisor", promisor_msg, final_pack_name,
|
2018-02-15 22:55:47 +00:00
|
|
|
hash, NULL);
|
2006-10-29 09:41:59 +00:00
|
|
|
|
2021-09-09 23:24:49 +00:00
|
|
|
rename_tmp_packfile(&final_pack_name, curr_pack_name, &pack_name,
|
|
|
|
hash, "pack", from_stdin);
|
|
|
|
if (curr_rev_index_name)
|
|
|
|
rename_tmp_packfile(&final_rev_index_name, curr_rev_index_name,
|
|
|
|
&rev_index_name, hash, "rev", 1);
|
2021-09-09 23:24:53 +00:00
|
|
|
rename_tmp_packfile(&final_index_name, curr_index_name, &index_name,
|
|
|
|
hash, "idx", 1);
|
2021-01-25 23:37:26 +00:00
|
|
|
|
index-pack: handle --strict checks of non-repo packs
Commit 73c3f0f704 (index-pack: check .gitmodules files with
--strict, 2018-05-04) added a call to add_packed_git(), with
the intent that the newly-indexed objects would be available
to the process when we run fsck_finish(). But that's not
what add_packed_git() does. It only allocates the struct,
and you must install_packed_git() on the result. So that
call was effectively doing nothing (except leaking a
struct).
But wait, we passed all of the tests! Does that mean we
don't need the call at all?
For normal cases, no. When we run "index-pack --stdin"
inside a repository, we write the new pack into the object
directory. If fsck_finish() needs to access one of the new
objects, then our initial lookup will fail to find it, but
we'll follow up by running reprepare_packed_git() and
looking again. That logic was meant to handle somebody else
repacking simultaneously, but it ends up working for us
here.
But there is a case that does need this, that we were not
testing. You can run "git index-pack foo.pack" on any file,
even when it is not inside the object directory. Or you may
not even be in a repository at all! This case fails without
doing the proper install_packed_git() call.
We can make this work by adding the install call.
Note that we should be prepared to handle add_packed_git()
failing. We can just silently ignore this case, though. If
fsck_finish() later needs the objects and they're not
available, it will complain itself. And if it doesn't
(because we were able to resolve the whole fsck in the first
pass), then it actually isn't an interesting error at all.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-05-31 22:45:31 +00:00
|
|
|
if (do_fsck_object) {
|
|
|
|
struct packed_git *p;
|
|
|
|
p = add_packed_git(final_index_name, strlen(final_index_name), 0);
|
|
|
|
if (p)
|
2018-06-13 19:50:46 +00:00
|
|
|
install_packed_git(the_repository, p);
|
index-pack: handle --strict checks of non-repo packs
Commit 73c3f0f704 (index-pack: check .gitmodules files with
--strict, 2018-05-04) added a call to add_packed_git(), with
the intent that the newly-indexed objects would be available
to the process when we run fsck_finish(). But that's not
what add_packed_git() does. It only allocates the struct,
and you must install_packed_git() on the result. So that
call was effectively doing nothing (except leaking a
struct).
But wait, we passed all of the tests! Does that mean we
don't need the call at all?
For normal cases, no. When we run "index-pack --stdin"
inside a repository, we write the new pack into the object
directory. If fsck_finish() needs to access one of the new
objects, then our initial lookup will fail to find it, but
we'll follow up by running reprepare_packed_git() and
looking again. That logic was meant to handle somebody else
repacking simultaneously, but it ends up working for us
here.
But there is a case that does need this, that we were not
testing. You can run "git index-pack foo.pack" on any file,
even when it is not inside the object directory. Or you may
not even be in a repository at all! This case fails without
doing the proper install_packed_git() call.
We can make this work by adding the install call.
Note that we should be prepared to handle add_packed_git()
failing. We can just silently ignore this case, though. If
fsck_finish() later needs the objects and they're not
available, it will complain itself. And if it doesn't
(because we were able to resolve the whole fsck in the first
pass), then it actually isn't an interesting error at all.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-05-31 22:45:31 +00:00
|
|
|
}
|
index-pack: check .gitmodules files with --strict
Now that the internal fsck code has all of the plumbing we
need, we can start checking incoming .gitmodules files.
Naively, it seems like we would just need to add a call to
fsck_finish() after we've processed all of the objects. And
that would be enough to cover the initial test included
here. But there are two extra bits:
1. We currently don't bother calling fsck_object() at all
for blobs, since it has traditionally been a noop. We'd
actually catch these blobs in fsck_finish() at the end,
but it's more efficient to check them when we already
have the object loaded in memory.
2. The second pass done by fsck_finish() needs to access
the objects, but we're actually indexing the pack in
this process. In theory we could give the fsck code a
special callback for accessing the in-pack data, but
it's actually quite tricky:
a. We don't have an internal efficient index mapping
oids to packfile offsets. We only generate it on
the fly as part of writing out the .idx file.
b. We'd still have to reconstruct deltas, which means
we'd basically have to replicate all of the
reading logic in packfile.c.
Instead, let's avoid running fsck_finish() until after
we've written out the .idx file, and then just add it
to our internal packed_git list.
This does mean that the objects are "in the repository"
before we finish our fsck checks. But unpack-objects
already exhibits this same behavior, and it's an
acceptable tradeoff here for the same reason: the
quarantine mechanism means that pushes will be
fully protected.
In addition to a basic push test in t7415, we add a sneaky
pack that reverses the usual object order in the pack,
requiring that index-pack access the tree and blob during
the "finish" step.
This already works for unpack-objects (since it will have
written out loose objects), but we'll check it with this
sneaky pack for good measure.
Signed-off-by: Jeff King <peff@peff.net>
2018-05-04 23:45:01 +00:00
|
|
|
|
2006-11-01 22:06:25 +00:00
|
|
|
if (!from_stdin) {
|
2019-08-18 20:04:23 +00:00
|
|
|
printf("%s\n", hash_to_hex(hash));
|
2006-11-01 22:06:25 +00:00
|
|
|
} else {
|
2017-03-28 19:46:50 +00:00
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
|
2019-08-18 20:04:23 +00:00
|
|
|
strbuf_addf(&buf, "%s\t%s\n", report, hash_to_hex(hash));
|
2017-03-28 19:46:50 +00:00
|
|
|
write_or_die(1, buf.buf, buf.len);
|
|
|
|
strbuf_release(&buf);
|
2006-11-01 22:06:25 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Let's just mimic git-unpack-objects here and write
|
|
|
|
* the last part of the input buffer to stdout.
|
|
|
|
*/
|
|
|
|
while (input_len) {
|
|
|
|
err = xwrite(1, input_buffer + input_offset, input_len);
|
|
|
|
if (err <= 0)
|
|
|
|
break;
|
|
|
|
input_len -= err;
|
|
|
|
input_offset += err;
|
|
|
|
}
|
|
|
|
}
|
2017-03-16 14:27:15 +00:00
|
|
|
|
2021-01-25 23:37:26 +00:00
|
|
|
strbuf_release(&rev_index_name);
|
index-pack: make pointer-alias fallbacks safer
The final() function accepts a NULL value for certain
parameters, and falls back to writing into a reusable "name"
buffer, and then either:
1. For "keep_name", requiring all uses to do "keep_name ?
keep_name : name.buf". This is awkward, and it's easy
to accidentally look at the maybe-NULL keep_name.
2. For "final_index_name" and "final_pack_name", aliasing
those pointers to the "name" buffer. This is easier to
use, but the aliased pointers become invalid after the
buffer is reused (this isn't a bug now, but it's a
potential pitfall).
One way to make this safer would be to introduce an extra
pointer to do the aliasing, and have its lifetime match the
validity of the "name" buffer. But it's still easy to
accidentally use the wrong name (i.e., to use
"final_pack_name" instead of the aliased pointer).
Instead, let's use three separate buffers that will remain
valid through the function. That makes it safe to alias the
pointers and use them consistently. The extra allocations
shouldn't matter, as this function is not performance
sensitive.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-16 14:27:20 +00:00
|
|
|
strbuf_release(&index_name);
|
|
|
|
strbuf_release(&pack_name);
|
2005-10-12 19:01:31 +00:00
|
|
|
}
|
|
|
|
|
config: add ctx arg to config_fn_t
Add a new "const struct config_context *ctx" arg to config_fn_t to hold
additional information about the config iteration operation.
config_context has a "struct key_value_info kvi" member that holds
metadata about the config source being read (e.g. what kind of config
source it is, the filename, etc). In this series, we're only interested
in .kvi, so we could have just used "struct key_value_info" as an arg,
but config_context makes it possible to add/adjust members in the future
without changing the config_fn_t signature. We could also consider other
ways of organizing the args (e.g. moving the config name and value into
config_context or key_value_info), but in my experiments, the
incremental benefit doesn't justify the added complexity (e.g. a
config_fn_t will sometimes invoke another config_fn_t but with a
different config value).
In subsequent commits, the .kvi member will replace the global "struct
config_reader" in config.c, making config iteration a global-free
operation. It requires much more work for the machinery to provide
meaningful values of .kvi, so for now, merely change the signature and
call sites, pass NULL as a placeholder value, and don't rely on the arg
in any meaningful way.
Most of the changes are performed by
contrib/coccinelle/config_fn_ctx.pending.cocci, which, for every
config_fn_t:
- Modifies the signature to accept "const struct config_context *ctx"
- Passes "ctx" to any inner config_fn_t, if needed
- Adds UNUSED attributes to "ctx", if needed
Most config_fn_t instances are easily identified by seeing if they are
called by the various config functions. Most of the remaining ones are
manually named in the .cocci patch. Manual cleanups are still needed,
but the majority of it is trivial; it's either adjusting config_fn_t
that the .cocci patch didn't catch, or adding forward declarations of
"struct config_context ctx" to make the signatures make sense.
The non-trivial changes are in cases where we are invoking a config_fn_t
outside of config machinery, and we now need to decide what value of
"ctx" to pass. These cases are:
- trace2/tr2_cfg.c:tr2_cfg_set_fl()
This is indirectly called by git_config_set() so that the trace2
machinery can notice the new config values and update its settings
using the tr2 config parsing function, i.e. tr2_cfg_cb().
- builtin/checkout.c:checkout_main()
This calls git_xmerge_config() as a shorthand for parsing a CLI arg.
This might be worth refactoring away in the future, since
git_xmerge_config() can call git_default_config(), which can do much
more than just parsing.
Handle them by creating a KVI_INIT macro that initializes "struct
key_value_info" to a reasonable default, and use that to construct the
"ctx" arg.
Signed-off-by: Glen Choo <chooglen@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-06-28 19:26:22 +00:00
|
|
|
static int git_index_pack_config(const char *k, const char *v,
|
|
|
|
const struct config_context *ctx, void *cb)
|
2007-11-02 03:26:04 +00:00
|
|
|
{
|
2011-02-25 23:43:25 +00:00
|
|
|
struct pack_idx_option *opts = cb;
|
|
|
|
|
2007-11-02 03:26:04 +00:00
|
|
|
if (!strcmp(k, "pack.indexversion")) {
|
config: pass kvi to die_bad_number()
Plumb "struct key_value_info" through all code paths that end in
die_bad_number(), which lets us remove the helper functions that read
analogous values from "struct config_reader". As a result, nothing reads
config_reader.config_kvi any more, so remove that too.
In config.c, this requires changing the signature of
git_configset_get_value() to 'return' "kvi" in an out parameter so that
git_configset_get_<type>() can pass it to git_config_<type>(). Only
numeric types will use "kvi", so for non-numeric types (e.g.
git_configset_get_string()), pass NULL to indicate that the out
parameter isn't needed.
Outside of config.c, config callbacks now need to pass "ctx->kvi" to any
of the git_config_<type>() functions that parse a config string into a
number type. Included is a .cocci patch to make that refactor.
The only exceptional case is builtin/config.c, where git_config_<type>()
is called outside of a config callback (namely, on user-provided input),
so config source information has never been available. In this case,
die_bad_number() defaults to a generic, but perfectly descriptive
message. Let's provide a safe, non-NULL for "kvi" anyway, but make sure
not to change the message.
Signed-off-by: Glen Choo <chooglen@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-06-28 19:26:27 +00:00
|
|
|
opts->version = git_config_int(k, v, ctx->kvi);
|
2011-02-25 23:43:25 +00:00
|
|
|
if (opts->version > 2)
|
2022-06-17 10:03:09 +00:00
|
|
|
die(_("bad pack.indexVersion=%"PRIu32), opts->version);
|
2007-11-02 03:26:04 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2012-05-06 12:31:55 +00:00
|
|
|
if (!strcmp(k, "pack.threads")) {
|
config: pass kvi to die_bad_number()
Plumb "struct key_value_info" through all code paths that end in
die_bad_number(), which lets us remove the helper functions that read
analogous values from "struct config_reader". As a result, nothing reads
config_reader.config_kvi any more, so remove that too.
In config.c, this requires changing the signature of
git_configset_get_value() to 'return' "kvi" in an out parameter so that
git_configset_get_<type>() can pass it to git_config_<type>(). Only
numeric types will use "kvi", so for non-numeric types (e.g.
git_configset_get_string()), pass NULL to indicate that the out
parameter isn't needed.
Outside of config.c, config callbacks now need to pass "ctx->kvi" to any
of the git_config_<type>() functions that parse a config string into a
number type. Included is a .cocci patch to make that refactor.
The only exceptional case is builtin/config.c, where git_config_<type>()
is called outside of a config callback (namely, on user-provided input),
so config source information has never been available. In this case,
die_bad_number() defaults to a generic, but perfectly descriptive
message. Let's provide a safe, non-NULL for "kvi" anyway, but make sure
not to change the message.
Signed-off-by: Glen Choo <chooglen@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-06-28 19:26:27 +00:00
|
|
|
nr_threads = git_config_int(k, v, ctx->kvi);
|
2012-05-06 12:31:55 +00:00
|
|
|
if (nr_threads < 0)
|
2012-08-31 12:13:04 +00:00
|
|
|
die(_("invalid number of threads specified (%d)"),
|
2012-05-06 12:31:55 +00:00
|
|
|
nr_threads);
|
2018-11-03 08:48:40 +00:00
|
|
|
if (!HAVE_THREADS && nr_threads != 1) {
|
2012-08-31 12:13:04 +00:00
|
|
|
warning(_("no threads support, ignoring %s"), k);
|
2018-11-03 08:48:40 +00:00
|
|
|
nr_threads = 1;
|
|
|
|
}
|
2012-05-06 12:31:55 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2021-01-25 23:37:26 +00:00
|
|
|
if (!strcmp(k, "pack.writereverseindex")) {
|
|
|
|
if (git_config_bool(k, v))
|
|
|
|
opts->flags |= WRITE_REV;
|
|
|
|
else
|
|
|
|
opts->flags &= ~WRITE_REV;
|
|
|
|
}
|
config: add ctx arg to config_fn_t
Add a new "const struct config_context *ctx" arg to config_fn_t to hold
additional information about the config iteration operation.
config_context has a "struct key_value_info kvi" member that holds
metadata about the config source being read (e.g. what kind of config
source it is, the filename, etc). In this series, we're only interested
in .kvi, so we could have just used "struct key_value_info" as an arg,
but config_context makes it possible to add/adjust members in the future
without changing the config_fn_t signature. We could also consider other
ways of organizing the args (e.g. moving the config name and value into
config_context or key_value_info), but in my experiments, the
incremental benefit doesn't justify the added complexity (e.g. a
config_fn_t will sometimes invoke another config_fn_t but with a
different config value).
In subsequent commits, the .kvi member will replace the global "struct
config_reader" in config.c, making config iteration a global-free
operation. It requires much more work for the machinery to provide
meaningful values of .kvi, so for now, merely change the signature and
call sites, pass NULL as a placeholder value, and don't rely on the arg
in any meaningful way.
Most of the changes are performed by
contrib/coccinelle/config_fn_ctx.pending.cocci, which, for every
config_fn_t:
- Modifies the signature to accept "const struct config_context *ctx"
- Passes "ctx" to any inner config_fn_t, if needed
- Adds UNUSED attributes to "ctx", if needed
Most config_fn_t instances are easily identified by seeing if they are
called by the various config functions. Most of the remaining ones are
manually named in the .cocci patch. Manual cleanups are still needed,
but the majority of it is trivial; it's either adjusting config_fn_t
that the .cocci patch didn't catch, or adding forward declarations of
"struct config_context ctx" to make the signatures make sense.
The non-trivial changes are in cases where we are invoking a config_fn_t
outside of config machinery, and we now need to decide what value of
"ctx" to pass. These cases are:
- trace2/tr2_cfg.c:tr2_cfg_set_fl()
This is indirectly called by git_config_set() so that the trace2
machinery can notice the new config values and update its settings
using the tr2 config parsing function, i.e. tr2_cfg_cb().
- builtin/checkout.c:checkout_main()
This calls git_xmerge_config() as a shorthand for parsing a CLI arg.
This might be worth refactoring away in the future, since
git_xmerge_config() can call git_default_config(), which can do much
more than just parsing.
Handle them by creating a KVI_INIT macro that initializes "struct
key_value_info" to a reasonable default, and use that to construct the
"ctx" arg.
Signed-off-by: Glen Choo <chooglen@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-06-28 19:26:22 +00:00
|
|
|
return git_default_config(k, v, ctx, cb);
|
2007-11-02 03:26:04 +00:00
|
|
|
}
|
|
|
|
|
2011-02-26 00:55:26 +00:00
|
|
|
static int cmp_uint32(const void *a_, const void *b_)
|
|
|
|
{
|
|
|
|
uint32_t a = *((uint32_t *)a_);
|
|
|
|
uint32_t b = *((uint32_t *)b_);
|
|
|
|
|
|
|
|
return (a < b) ? -1 : (a != b);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void read_v2_anomalous_offsets(struct packed_git *p,
|
|
|
|
struct pack_idx_option *opts)
|
|
|
|
{
|
|
|
|
const uint32_t *idx1, *idx2;
|
|
|
|
uint32_t i;
|
|
|
|
|
|
|
|
/* The address of the 4-byte offset table */
|
2020-05-25 19:59:10 +00:00
|
|
|
idx1 = (((const uint32_t *)((const uint8_t *)p->index_data + p->crc_offset))
|
compute pack .idx byte offsets using size_t
A pack and its matching .idx file are limited to 2^32 objects, because
the pack format contains a 32-bit field to store the number of objects.
Hence we use uint32_t in the code.
But the byte count of even a .idx file can be much larger than that,
because it stores at least a hash and an offset for each object. So
using SHA-1, a v2 .idx file will cross the 4GB boundary at 153,391,650
objects. This confuses load_idx(), which computes the minimum size like
this:
unsigned long min_size = 8 + 4*256 + nr*(hashsz + 4 + 4) + hashsz + hashsz;
Even though min_size will be big enough on most 64-bit platforms, the
actual arithmetic is done as a uint32_t, resulting in a truncation. We
actually exceed that min_size, but then we do:
unsigned long max_size = min_size;
if (nr)
max_size += (nr - 1)*8;
to account for the variable-sized table. That computation doesn't
overflow quite so low, but with the truncation for min_size, we end up
with a max_size that is much smaller than our actual size. So we
complain that the idx is invalid, and can't find any of its objects.
We can fix this case by casting "nr" to a size_t, which will do the
multiplication in 64-bits (assuming you're on a 64-bit platform; this
will never work on a 32-bit system since we couldn't map the whole .idx
anyway). Likewise, we don't have to worry about further additions,
because adding a smaller number to a size_t will convert the other side
to a size_t.
A few notes:
- obviously we could just declare "nr" as a size_t in the first place
(and likewise, packed_git.num_objects). But it's conceptually a
uint32_t because of the on-disk format, and we correctly treat it
that way in other contexts that don't need to compute byte offsets
(e.g., iterating over the set of objects should and generally does
use a uint32_t). Switching to size_t would make all of those other
cases look wrong.
- it could be argued that the proper type is off_t to represent the
file offset. But in practice the .idx file must fit within memory,
because we mmap the whole thing. And the rest of the code (including
the idx_size variable we're comparing against) uses size_t.
- we'll add the same cast to the max_size arithmetic line. Even though
we're adding to a larger type, which will convert our result, the
multiplication is still done as a 32-bit value and can itself
overflow. I didn't check this with my test case, since it would need
an even larger pack (~530M objects), but looking at compiler output
shows that it works this way. The standard should agree, but I
couldn't find anything explicit in 6.3.1.8 ("usual arithmetic
conversions").
The case in load_idx() was the most immediate one that I was able to
trigger. After fixing it, looking up actual objects (including the very
last one in sha1 order) works in a test repo with 153,725,110 objects.
That's because bsearch_hash() works with uint32_t entry indices, and the
actual byte access:
int cmp = hashcmp(table + mi * stride, sha1);
is done with "stride" as a size_t, causing the uint32_t "mi" to be
promoted to a size_t. This is the way most code will access the index
data.
However, I audited all of the other byte-wise accesses of
packed_git.index_data, and many of the others are suspect (they are
similar to the max_size one, where we are adding to a properly sized
offset or directly to a pointer, but the multiplication in the
sub-expression can overflow). I didn't trigger any of these in practice,
but I believe they're potential problems, and certainly adding in the
cast is not going to hurt anything here.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-11-13 05:06:48 +00:00
|
|
|
+ (size_t)p->num_objects /* CRC32 table */
|
2011-02-26 00:55:26 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
/* The address of the 8-byte offset table */
|
|
|
|
idx2 = idx1 + p->num_objects;
|
|
|
|
|
|
|
|
for (i = 0; i < p->num_objects; i++) {
|
|
|
|
uint32_t off = ntohl(idx1[i]);
|
|
|
|
if (!(off & 0x80000000))
|
|
|
|
continue;
|
|
|
|
off = off & 0x7fffffff;
|
2016-02-25 14:22:52 +00:00
|
|
|
check_pack_index_ptr(p, &idx2[off * 2]);
|
2011-02-26 00:55:26 +00:00
|
|
|
if (idx2[off * 2])
|
|
|
|
continue;
|
|
|
|
/*
|
|
|
|
* The real offset is ntohl(idx2[off * 2]) in high 4
|
|
|
|
* octets, and ntohl(idx2[off * 2 + 1]) in low 4
|
|
|
|
* octets. But idx2[off * 2] is Zero!!!
|
|
|
|
*/
|
|
|
|
ALLOC_GROW(opts->anomaly, opts->anomaly_nr + 1, opts->anomaly_alloc);
|
|
|
|
opts->anomaly[opts->anomaly_nr++] = ntohl(idx2[off * 2 + 1]);
|
|
|
|
}
|
|
|
|
|
2016-09-29 23:40:14 +00:00
|
|
|
QSORT(opts->anomaly, opts->anomaly_nr, cmp_uint32);
|
2011-02-26 00:55:26 +00:00
|
|
|
}
|
|
|
|
|
2011-02-03 01:29:01 +00:00
|
|
|
static void read_idx_option(struct pack_idx_option *opts, const char *pack_name)
|
|
|
|
{
|
|
|
|
struct packed_git *p = add_packed_git(pack_name, strlen(pack_name), 1);
|
|
|
|
|
|
|
|
if (!p)
|
2012-04-23 12:30:29 +00:00
|
|
|
die(_("Cannot open existing pack file '%s'"), pack_name);
|
2011-02-03 01:29:01 +00:00
|
|
|
if (open_pack_index(p))
|
2012-04-23 12:30:29 +00:00
|
|
|
die(_("Cannot open existing pack idx file for '%s'"), pack_name);
|
2011-02-03 01:29:01 +00:00
|
|
|
|
|
|
|
/* Read the attributes from the existing idx file */
|
|
|
|
opts->version = p->index_version;
|
|
|
|
|
2011-02-26 00:55:26 +00:00
|
|
|
if (opts->version == 2)
|
|
|
|
read_v2_anomalous_offsets(p, opts);
|
|
|
|
|
2011-02-03 01:29:01 +00:00
|
|
|
/*
|
|
|
|
* Get rid of the idx file as we do not need it anymore.
|
|
|
|
* NEEDSWORK: extract this bit from free_pack_by_name() in
|
2020-12-31 11:56:21 +00:00
|
|
|
* object-file.c, perhaps? It shouldn't matter very much as we
|
2011-02-03 01:29:01 +00:00
|
|
|
* know we haven't installed this pack (hence we never have
|
|
|
|
* read anything from it).
|
|
|
|
*/
|
|
|
|
close_pack_index(p);
|
|
|
|
free(p);
|
|
|
|
}
|
|
|
|
|
2011-06-03 22:32:15 +00:00
|
|
|
static void show_pack_info(int stat_only)
|
|
|
|
{
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
int i, baseobjects = nr_objects - nr_ref_deltas - nr_ofs_deltas;
|
2011-06-03 22:32:16 +00:00
|
|
|
unsigned long *chain_histogram = NULL;
|
|
|
|
|
|
|
|
if (deepest_delta)
|
2021-03-13 16:17:22 +00:00
|
|
|
CALLOC_ARRAY(chain_histogram, deepest_delta);
|
2011-06-03 22:32:16 +00:00
|
|
|
|
2011-06-03 22:32:15 +00:00
|
|
|
for (i = 0; i < nr_objects; i++) {
|
|
|
|
struct object_entry *obj = &objects[i];
|
|
|
|
|
2011-06-03 22:32:16 +00:00
|
|
|
if (is_delta_type(obj->type))
|
2015-02-26 10:52:07 +00:00
|
|
|
chain_histogram[obj_stat[i].delta_depth - 1]++;
|
2011-06-03 22:32:15 +00:00
|
|
|
if (stat_only)
|
|
|
|
continue;
|
2018-11-11 07:05:04 +00:00
|
|
|
printf("%s %-6s %"PRIuMAX" %"PRIuMAX" %"PRIuMAX,
|
2017-05-06 22:10:11 +00:00
|
|
|
oid_to_hex(&obj->idx.oid),
|
2018-11-11 07:05:04 +00:00
|
|
|
type_name(obj->real_type), (uintmax_t)obj->size,
|
|
|
|
(uintmax_t)(obj[1].idx.offset - obj->idx.offset),
|
2011-06-03 22:32:15 +00:00
|
|
|
(uintmax_t)obj->idx.offset);
|
|
|
|
if (is_delta_type(obj->type)) {
|
2015-02-26 10:52:07 +00:00
|
|
|
struct object_entry *bobj = &objects[obj_stat[i].base_object_no];
|
2017-05-06 22:10:11 +00:00
|
|
|
printf(" %u %s", obj_stat[i].delta_depth,
|
|
|
|
oid_to_hex(&bobj->idx.oid));
|
2011-06-03 22:32:15 +00:00
|
|
|
}
|
|
|
|
putchar('\n');
|
|
|
|
}
|
2011-06-03 22:32:16 +00:00
|
|
|
|
|
|
|
if (baseobjects)
|
2012-04-23 12:30:29 +00:00
|
|
|
printf_ln(Q_("non delta: %d object",
|
|
|
|
"non delta: %d objects",
|
|
|
|
baseobjects),
|
|
|
|
baseobjects);
|
2011-06-03 22:32:16 +00:00
|
|
|
for (i = 0; i < deepest_delta; i++) {
|
|
|
|
if (!chain_histogram[i])
|
|
|
|
continue;
|
2012-04-23 12:30:29 +00:00
|
|
|
printf_ln(Q_("chain length = %d: %lu object",
|
|
|
|
"chain length = %d: %lu objects",
|
|
|
|
chain_histogram[i]),
|
|
|
|
i + 1,
|
|
|
|
chain_histogram[i]);
|
2011-06-03 22:32:16 +00:00
|
|
|
}
|
index-pack: fix memory leaks
Fix various memory leaks in "git index-pack", due to how tightly
coupled this command is with the revision walking this doesn't make
any new tests pass.
But e.g. this now passes, and had several failures before, i.e. we
still have failures in tests 3, 5 etc., which are being skipped here.
./t5300-pack-object.sh --run=1-2,4,6-27,30-42
It is a bit odd that we'll free "opts.anomaly", since the "opts" is a
"struct pack_idx_option" declared in pack.h. In pack-write.c there's a
reset_pack_idx_option(), but it only wipes the contents, but doesn't
free() anything.
Doing this here in cmd_index_pack() is correct because while the
struct is declared in pack.h, this code in builtin/index-pack.c (in
read_v2_anomalous_offsets()) is what allocates the "opts.anomaly", so
we should also free it here.
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-03-04 18:32:04 +00:00
|
|
|
free(chain_histogram);
|
2011-06-03 22:32:15 +00:00
|
|
|
}
|
|
|
|
|
2010-01-22 15:55:19 +00:00
|
|
|
int cmd_index_pack(int argc, const char **argv, const char *prefix)
|
2005-10-12 19:01:31 +00:00
|
|
|
{
|
2021-01-25 23:37:26 +00:00
|
|
|
int i, fix_thin_pack = 0, verify = 0, stat_only = 0, rev_index;
|
2014-03-25 13:41:41 +00:00
|
|
|
const char *curr_index;
|
2021-01-25 23:37:26 +00:00
|
|
|
const char *curr_rev_index = NULL;
|
|
|
|
const char *index_name = NULL, *pack_name = NULL, *rev_index_name = NULL;
|
2017-12-05 16:58:48 +00:00
|
|
|
const char *keep_msg = NULL;
|
introduce fetch-object: fetch one promisor object
Introduce fetch-object, providing the ability to fetch one object from a
promisor remote.
This uses fetch-pack. To do this, the transport mechanism has been
updated with 2 flags, "from-promisor" to indicate that the resulting
pack comes from a promisor remote (and thus should be annotated as such
by index-pack), and "no-dependents" to indicate that only the objects
themselves need to be fetched (but fetching additional objects is
nevertheless safe).
Whenever "no-dependents" is used, fetch-pack will refrain from using any
object flags, because it is most likely invoked as part of a dynamic
object fetch by another Git command (which may itself use object flags).
An alternative to this is to leave fetch-pack alone, and instead update
the allocation of flags so that fetch-pack's flags never overlap with
any others, but this will end up shrinking the number of flags available
to nearly every other Git command (that is, every Git command that
accesses objects), so the approach in this commit was used instead.
This will be tested in a subsequent commit.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-12-05 16:58:49 +00:00
|
|
|
const char *promisor_msg = NULL;
|
2017-12-05 16:58:48 +00:00
|
|
|
struct strbuf index_name_buf = STRBUF_INIT;
|
2021-01-25 23:37:26 +00:00
|
|
|
struct strbuf rev_index_name_buf = STRBUF_INIT;
|
2007-06-01 19:18:05 +00:00
|
|
|
struct pack_idx_entry **idx_objects;
|
2011-02-25 23:43:25 +00:00
|
|
|
struct pack_idx_option opts;
|
2018-02-01 02:18:39 +00:00
|
|
|
unsigned char pack_hash[GIT_MAX_RAWSZ];
|
2013-05-26 01:16:17 +00:00
|
|
|
unsigned foreign_nr = 1; /* zero is a "good" value, assume bad */
|
receive-pack: send keepalives during quiet periods
After a client has sent us the complete pack, we may spend
some time processing the data and running hooks. If the
client asked us to be quiet, receive-pack won't send any
progress data during the index-pack or connectivity-check
steps. And hooks may or may not produce their own progress
output. In these cases, the network connection is totally
silent from both ends.
Git itself doesn't care about this (it will wait forever),
but other parts of the system (e.g., firewalls,
load-balancers, etc) might hang up the connection. So we'd
like to send some sort of keepalive to let the network and
the client side know that we're still alive and processing.
We can use the same trick we did in 05e9515 (upload-pack:
send keepalive packets during pack computation, 2013-09-08).
Namely, we will send an empty sideband data packet every `N`
seconds that we do not relay any stderr data over the
sideband channel. As with 05e9515, this means that we won't
bother sending keepalives when there's actual progress data,
but will kick in when it has been disabled (or if there is a
lull in the progress data).
The concept is simple, but the details are subtle enough
that they need discussing here.
Before the client sends us the pack, we don't want to do any
keepalives. We'll have sent our ref advertisement, and we're
waiting for them to send us the pack (and tell us that they
support sidebands at all).
While we're receiving the pack from the client (or waiting
for it to start), there's no need for keepalives; it's up to
them to keep the connection active by sending data.
Moreover, it would be wrong for us to do so. When we are the
server in the smart-http protocol, we must treat our
connection as half-duplex. So any keepalives we send while
receiving the pack would potentially be buffered by the
webserver. Not only does this make them useless (since they
would not be delivered in a timely manner), but it could
actually cause a deadlock if we fill up the buffer with
keepalives. (It wouldn't be wrong to send keepalives in this
phase for a full-duplex connection like ssh; it's simply
pointless, as it is the client's responsibility to speak).
As soon as we've gotten all of the pack data, then the
client is waiting for us to speak, and we should start
keepalives immediately. From here until the end of the
connection, we send one any time we are not otherwise
sending data.
But there's a catch. Receive-pack doesn't know the moment
we've gotten all the data. It passes the descriptor to
index-pack, who reads all of the data, and then starts
resolving the deltas. We have to communicate that back.
To make this work, we instruct the sideband muxer to enable
keepalives in three phases:
1. In the beginning, not at all.
2. While reading from index-pack, wait for a signal
indicating end-of-input, and then start them.
3. Afterwards, always.
The signal from index-pack in phase 2 has to come over the
stderr channel which the muxer is reading. We can't use an
extra pipe because the portable run-command interface only
gives us stderr and stdout.
Stdout is already used to pass the .keep filename back to
receive-pack. We could also send a signal there, but then we
would find out about it in the main thread. And the
keepalive needs to be done by the async muxer thread (since
it's the one writing sideband data back to the client). And
we can't reliably signal the async thread from the main
thread, because the async code sometimes uses threads and
sometimes uses forked processes.
Therefore the signal must come over the stderr channel,
where it may be interspersed with other random
human-readable messages from index-pack. This patch makes
the signal a single NUL byte. This is easy to parse, should
not appear in any normal stderr output, and we don't have to
worry about any timing issues (like seeing half the signal
bytes in one read(), and half in a subsequent one).
This is a bit ugly, but it's simple to code and should work
reliably.
Another option would be to stop using an async thread for
muxing entirely, and just poll() both stderr and stdout of
index-pack from the main thread. This would work for
index-pack (because we aren't doing anything useful in the
main thread while it runs anyway). But it would make the
connectivity check and the hook muxers much more
complicated, as they need to simultaneously feed the
sub-programs while reading their stderr.
The index-pack phase is the only one that needs this
signaling, so it could simply behave differently than the
other two. That would mean having two separate
implementations of copy_to_sideband (and the keepalive
code), though. And it still doesn't get rid of the
signaling; it just means we can write a nicer message like
"END_OF_INPUT" or something on stdout, since we don't have
to worry about separating it from the stderr cruft.
One final note: this signaling trick is only done with
index-pack, not with unpack-objects. There's no point in
doing it for the latter, because by definition it only kicks
in for a small number of objects, where keepalives are not
as useful (and this conveniently lets us avoid duplicating
the implementation).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-07-15 10:43:47 +00:00
|
|
|
int report_end_of_input = 0;
|
2020-06-19 17:55:52 +00:00
|
|
|
int hash_algo = 0;
|
2005-10-12 19:01:31 +00:00
|
|
|
|
2017-12-08 15:27:14 +00:00
|
|
|
/*
|
index-pack: prefetch missing REF_DELTA bases
When fetching, the client sends "have" commit IDs indicating that the
server does not need to send any object referenced by those commits,
reducing network I/O. When the client is a partial clone, the client
still sends "have"s in this way, even if it does not have every object
referenced by a commit it sent as "have".
If a server omits such an object, it is fine: the client could lazily
fetch that object before this fetch, and it can still do so after.
The issue is when the server sends a thin pack containing an object that
is a REF_DELTA against such a missing object: index-pack fails to fix
the thin pack. When support for lazily fetching missing objects was
added in 8b4c0103a9 ("sha1_file: support lazily fetching missing
objects", 2017-12-08), support in index-pack was turned off in the
belief that it accesses the repo only to do hash collision checks.
However, this is not true: it also needs to access the repo to resolve
REF_DELTA bases.
Support for lazy fetching should still generally be turned off in
index-pack because it is used as part of the lazy fetching process
itself (if not, infinite loops may occur), but we do need to fetch the
REF_DELTA bases. (When fetching REF_DELTA bases, it is unlikely that
those are REF_DELTA themselves, because we do not send "have" when
making such fetches.)
To resolve this, prefetch all missing REF_DELTA bases before attempting
to resolve them. This both ensures that all bases are attempted to be
fetched, and ensures that we make only one request per index-pack
invocation, and not one request per missing object.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-05-14 21:10:55 +00:00
|
|
|
* index-pack never needs to fetch missing objects except when
|
|
|
|
* REF_DELTA bases are missing (which are explicitly handled). It only
|
|
|
|
* accesses the repo to do hash collision checks and to check which
|
|
|
|
* REF_DELTA bases need to be fetched.
|
2017-12-08 15:27:14 +00:00
|
|
|
*/
|
|
|
|
fetch_if_missing = 0;
|
|
|
|
|
2009-11-09 15:05:01 +00:00
|
|
|
if (argc == 2 && !strcmp(argv[1], "-h"))
|
|
|
|
usage(index_pack_usage);
|
|
|
|
|
2023-06-06 13:24:35 +00:00
|
|
|
disable_replace_refs();
|
2015-06-22 15:25:00 +00:00
|
|
|
fsck_options.walk = mark_link;
|
2010-08-12 14:18:12 +00:00
|
|
|
|
2011-02-25 23:43:25 +00:00
|
|
|
reset_pack_idx_option(&opts);
|
config: enable `pack.writeReverseIndex` by default
Back in e37d0b8730 (builtin/index-pack.c: write reverse indexes,
2021-01-25), Git learned how to read and write a pack's reverse index
from a file instead of in-memory.
A pack's reverse index is a mapping from pack position (that is, the
order that objects appear together in a ".pack") to their position in
lexical order (that is, the order that objects are listed in an ".idx"
file).
Reverse indexes are consulted often during pack-objects, as well as
during auxiliary operations that require mapping between pack offsets,
pack order, and index index.
They are useful in GitHub's infrastructure, where we have seen a
dramatic increase in performance when writing ".rev" files[1]. In
particular:
- an ~80% reduction in the time it takes to serve fetches on a popular
repository, Homebrew/homebrew-core.
- a ~60% reduction in the peak memory usage to serve fetches on that
same repository.
- a collective savings of ~35% in CPU time across all pack-objects
invocations serving fetches across all repositories in a single
datacenter.
Reverse indexes are also beneficial to end-users as well as forges. For
example, the time it takes to generate a pack containing the objects for
the 10 most recent commits in linux.git (representing a typical push) is
significantly faster when on-disk reverse indexes are available:
$ { git rev-parse HEAD && printf '^' && git rev-parse HEAD~10 } >in
$ hyperfine -L v false,true 'git.compile -c pack.readReverseIndex={v} pack-objects --delta-base-offset --revs --stdout <in >/dev/null'
Benchmark 1: git.compile -c pack.readReverseIndex=false pack-objects --delta-base-offset --revs --stdout <in >/dev/null
Time (mean ± σ): 543.0 ms ± 20.3 ms [User: 616.2 ms, System: 58.8 ms]
Range (min … max): 521.0 ms … 577.9 ms 10 runs
Benchmark 2: git.compile -c pack.readReverseIndex=true pack-objects --delta-base-offset --revs --stdout <in >/dev/null
Time (mean ± σ): 245.0 ms ± 11.4 ms [User: 335.6 ms, System: 31.3 ms]
Range (min … max): 226.0 ms … 259.6 ms 13 runs
Summary
'git.compile -c pack.readReverseIndex=true pack-objects --delta-base-offset --revs --stdout <in >/dev/null' ran
2.22 ± 0.13 times faster than 'git.compile -c pack.readReverseIndex=false pack-objects --delta-base-offset --revs --stdout <in >/dev/null'
The same is true of writing a pack containing the objects for the 30
most-recent commits:
$ { git rev-parse HEAD && printf '^' && git rev-parse HEAD~30 } >in
$ hyperfine -L v false,true 'git.compile -c pack.readReverseIndex={v} pack-objects --delta-base-offset --revs --stdout <in >/dev/null'
Benchmark 1: git.compile -c pack.readReverseIndex=false pack-objects --delta-base-offset --revs --stdout <in >/dev/null
Time (mean ± σ): 866.5 ms ± 16.2 ms [User: 1414.5 ms, System: 97.0 ms]
Range (min … max): 839.3 ms … 886.9 ms 10 runs
Benchmark 2: git.compile -c pack.readReverseIndex=true pack-objects --delta-base-offset --revs --stdout <in >/dev/null
Time (mean ± σ): 581.6 ms ± 10.2 ms [User: 1181.7 ms, System: 62.6 ms]
Range (min … max): 567.5 ms … 599.3 ms 10 runs
Summary
'git.compile -c pack.readReverseIndex=true pack-objects --delta-base-offset --revs --stdout <in >/dev/null' ran
1.49 ± 0.04 times faster than 'git.compile -c pack.readReverseIndex=false pack-objects --delta-base-offset --revs --stdout <in >/dev/null'
...and savings on trivial operations like computing the on-disk size of
a single (packed) object are even more dramatic:
$ git rev-parse HEAD >in
$ hyperfine -L v false,true 'git.compile -c pack.readReverseIndex={v} cat-file --batch-check="%(objectsize:disk)" <in'
Benchmark 1: git.compile -c pack.readReverseIndex=false cat-file --batch-check="%(objectsize:disk)" <in
Time (mean ± σ): 305.8 ms ± 11.4 ms [User: 264.2 ms, System: 41.4 ms]
Range (min … max): 290.3 ms … 331.1 ms 10 runs
Benchmark 2: git.compile -c pack.readReverseIndex=true cat-file --batch-check="%(objectsize:disk)" <in
Time (mean ± σ): 4.0 ms ± 0.3 ms [User: 1.7 ms, System: 2.3 ms]
Range (min … max): 1.6 ms … 4.6 ms 1155 runs
Summary
'git.compile -c pack.readReverseIndex=true cat-file --batch-check="%(objectsize:disk)" <in' ran
76.96 ± 6.25 times faster than 'git.compile -c pack.readReverseIndex=false cat-file --batch-check="%(objectsize:disk)" <in'
In the more than two years since e37d0b8730 was merged, Git's
implementation of on-disk reverse indexes has been thoroughly tested,
both from users enabling `pack.writeReverseIndexes`, and from GitHub's
deployment of the feature. The latter has been running without incident
for more than two years.
This patch changes Git's behavior to write on-disk reverse indexes by
default when indexing a pack, which should make the above operations
faster for everybody's Git installation after a repack.
(The previous commit explains some potential drawbacks of using on-disk
reverse indexes in certain limited circumstances, that essentially boil
down to a trade-off between time to generate, and time to access. For
those limited cases, the `pack.readReverseIndex` escape hatch can be
used).
[1]: https://github.blog/2021-04-29-scaling-monorepo-maintenance/#reverse-indexes
Signed-off-by: Taylor Blau <me@ttaylorr.com>
Acked-by: Derrick Stolee <derrickstolee@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-04-12 22:20:33 +00:00
|
|
|
opts.flags |= WRITE_REV;
|
2011-02-25 23:43:25 +00:00
|
|
|
git_config(git_index_pack_config, &opts);
|
2010-07-24 11:30:49 +00:00
|
|
|
if (prefix && chdir(prefix))
|
2012-04-23 12:30:29 +00:00
|
|
|
die(_("Cannot come back to cwd"));
|
2007-11-02 03:26:04 +00:00
|
|
|
|
t: invert `GIT_TEST_WRITE_REV_INDEX`
Back in e8c58f894b (t: support GIT_TEST_WRITE_REV_INDEX, 2021-01-25), we
added a test knob to conditionally enable writing a ".rev" file when
indexing a pack. At the time, this was used to ensure that the test
suite worked even when ".rev" files were written, which served as a
stress-test for the on-disk reverse index implementation.
Now that reading from on-disk ".rev" files is enabled by default, the
test knob `GIT_TEST_WRITE_REV_INDEX` no longer has any meaning.
We could get rid of the option entirely, but there would be no
convenient way to test Git when ".rev" files *aren't* in place.
Instead of getting rid of the option, invert its meaning to instead
disable writing ".rev" files, thereby running the test suite in a mode
where the reverse index is generated from scratch.
This ensures that, when GIT_TEST_NO_WRITE_REV_INDEX is set to some
spelling of "true", we are still running and exercising Git's behavior
when forced to generate reverse indexes from scratch. Do so by setting
it in the linux-TEST-vars CI run to ensure that we are maintaining good
coverage of this now-legacy code.
Signed-off-by: Taylor Blau <me@ttaylorr.com>
Acked-by: Derrick Stolee <derrickstolee@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-04-12 22:20:36 +00:00
|
|
|
if (git_env_bool(GIT_TEST_NO_WRITE_REV_INDEX, 0))
|
|
|
|
rev_index = 0;
|
2021-01-25 23:37:42 +00:00
|
|
|
else
|
|
|
|
rev_index = !!(opts.flags & (WRITE_REV_VERIFY | WRITE_REV));
|
2021-01-25 23:37:26 +00:00
|
|
|
|
2005-10-12 19:01:31 +00:00
|
|
|
for (i = 1; i < argc; i++) {
|
2010-01-22 15:55:19 +00:00
|
|
|
const char *arg = argv[i];
|
2005-10-12 19:01:31 +00:00
|
|
|
|
|
|
|
if (*arg == '-') {
|
2006-10-23 18:50:18 +00:00
|
|
|
if (!strcmp(arg, "--stdin")) {
|
|
|
|
from_stdin = 1;
|
2006-10-26 03:28:17 +00:00
|
|
|
} else if (!strcmp(arg, "--fix-thin")) {
|
|
|
|
fix_thin_pack = 1;
|
2017-12-09 20:40:08 +00:00
|
|
|
} else if (skip_to_optional_arg(arg, "--strict", &arg)) {
|
2015-06-22 15:25:31 +00:00
|
|
|
strict = 1;
|
|
|
|
do_fsck_object = 1;
|
|
|
|
fsck_set_msg_types(&fsck_options, arg);
|
2013-05-26 01:16:17 +00:00
|
|
|
} else if (!strcmp(arg, "--check-self-contained-and-connected")) {
|
|
|
|
strict = 1;
|
|
|
|
check_self_contained_and_connected = 1;
|
2024-02-01 01:38:02 +00:00
|
|
|
} else if (skip_to_optional_arg(arg, "--fsck-objects", &arg)) {
|
2018-03-14 18:42:40 +00:00
|
|
|
do_fsck_object = 1;
|
2024-02-01 01:38:02 +00:00
|
|
|
fsck_set_msg_types(&fsck_options, arg);
|
2011-02-03 01:29:01 +00:00
|
|
|
} else if (!strcmp(arg, "--verify")) {
|
|
|
|
verify = 1;
|
2011-06-03 22:32:15 +00:00
|
|
|
} else if (!strcmp(arg, "--verify-stat")) {
|
|
|
|
verify = 1;
|
2013-03-19 13:01:15 +00:00
|
|
|
show_stat = 1;
|
2011-06-03 22:32:15 +00:00
|
|
|
} else if (!strcmp(arg, "--verify-stat-only")) {
|
|
|
|
verify = 1;
|
2013-03-19 13:01:15 +00:00
|
|
|
show_stat = 1;
|
2011-06-03 22:32:15 +00:00
|
|
|
stat_only = 1;
|
2017-12-09 20:40:08 +00:00
|
|
|
} else if (skip_to_optional_arg(arg, "--keep", &keep_msg)) {
|
|
|
|
; /* nothing to do */
|
2018-02-13 21:39:03 +00:00
|
|
|
} else if (skip_to_optional_arg(arg, "--promisor", &promisor_msg)) {
|
|
|
|
; /* already parsed */
|
2013-11-30 20:55:40 +00:00
|
|
|
} else if (starts_with(arg, "--threads=")) {
|
2012-05-06 12:31:55 +00:00
|
|
|
char *end;
|
|
|
|
nr_threads = strtoul(arg+10, &end, 0);
|
|
|
|
if (!arg[10] || *end || nr_threads < 0)
|
|
|
|
usage(index_pack_usage);
|
2018-11-03 08:48:40 +00:00
|
|
|
if (!HAVE_THREADS && nr_threads != 1) {
|
|
|
|
warning(_("no threads support, ignoring %s"), arg);
|
|
|
|
nr_threads = 1;
|
|
|
|
}
|
2013-11-30 20:55:40 +00:00
|
|
|
} else if (starts_with(arg, "--pack_header=")) {
|
2006-11-01 22:06:20 +00:00
|
|
|
struct pack_header *hdr;
|
|
|
|
char *c;
|
|
|
|
|
|
|
|
hdr = (struct pack_header *)input_buffer;
|
|
|
|
hdr->hdr_signature = htonl(PACK_SIGNATURE);
|
|
|
|
hdr->hdr_version = htonl(strtoul(arg + 14, &c, 10));
|
|
|
|
if (*c != ',')
|
2012-04-23 12:30:29 +00:00
|
|
|
die(_("bad %s"), arg);
|
2006-11-01 22:06:20 +00:00
|
|
|
hdr->hdr_entries = htonl(strtoul(c + 1, &c, 10));
|
|
|
|
if (*c)
|
2012-04-23 12:30:29 +00:00
|
|
|
die(_("bad %s"), arg);
|
2006-11-01 22:06:20 +00:00
|
|
|
input_len = sizeof(*hdr);
|
2006-10-26 03:32:59 +00:00
|
|
|
} else if (!strcmp(arg, "-v")) {
|
|
|
|
verbose = 1;
|
index-pack: add --progress-title option
Add a --progress-title option to index-pack, when data is piped into
index-pack its progress is a proxy for whatever's feeding it data.
This option will allow us to set a more relevant progress bar title in
"git bundle unbundle", and is also used in my "bundle-uri" RFC
patches[1] by a new caller in fetch-pack.c.
The code change in cmd_index_pack() won't handle
"--progress-title=xyz", only "--progress-title xyz", and the "(i+1)"
style (as opposed to "i + 1") is a bit odd.
Not using the "--long-option=value" style is inconsistent with
existing long options handled by cmd_index_pack(), but makes the code
that needs to call it better (two strvec_push(), instead of needing a
strvec_pushf()). Since the option is internal-only the inconsistency
shouldn't matter.
I'm copying the pattern to handle it as-is from the handling of the
existing "-o" option in the same function, see 9cf6d3357aa (Add
git-index-pack utility, 2005-10-12) for its addition. That's a short
option, but the code to implement the two is the same in functionality
and style. Eventually we'd like to migrate all of this this to
parse_options(), which would make these differences in behavior go
away.
1. https://lore.kernel.org/git/RFC-cover-00.13-0000000000-20210805T150534Z-avarab@gmail.com/
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-09-05 07:34:44 +00:00
|
|
|
} else if (!strcmp(arg, "--progress-title")) {
|
|
|
|
if (progress_title || (i+1) >= argc)
|
|
|
|
usage(index_pack_usage);
|
|
|
|
progress_title = argv[++i];
|
index-pack: add flag for showing delta-resolution progress
The index-pack command has two progress meters: one for
"receiving objects", and one for "resolving deltas". You get
neither by default, or both with "-v".
But for a push through receive-pack, we would want only the
"resolving deltas" phase, _not_ the "receiving objects"
progress. There are two reasons for this.
One is simply that existing clients are already printing
"writing objects" progress at the same time. Arguably
"receiving" from the far end is more useful, because it
tells you what has actually gotten there, as opposed to what
might be stuck in a buffer somewhere between the client and
server. But that would require a protocol extension to tell
clients not to print their progress. Possible, but
complexity for little gain.
The second reason is much more important. In a full-duplex
connection like git-over-ssh, we can print progress while
the pack is incoming, and it will immediately get to the
client. But for a half-duplex connection like git-over-http,
we should not say anything until we have received the full
request. Anything we write is subject to being stuck in a
buffer by the webserver. Worse, we can end up in a deadlock
if that buffer fills up.
So our best bet is to avoid writing anything that isn't a
small fixed size until we've received the full pack.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-07-15 10:34:22 +00:00
|
|
|
} else if (!strcmp(arg, "--show-resolving-progress")) {
|
|
|
|
show_resolving_progress = 1;
|
receive-pack: send keepalives during quiet periods
After a client has sent us the complete pack, we may spend
some time processing the data and running hooks. If the
client asked us to be quiet, receive-pack won't send any
progress data during the index-pack or connectivity-check
steps. And hooks may or may not produce their own progress
output. In these cases, the network connection is totally
silent from both ends.
Git itself doesn't care about this (it will wait forever),
but other parts of the system (e.g., firewalls,
load-balancers, etc) might hang up the connection. So we'd
like to send some sort of keepalive to let the network and
the client side know that we're still alive and processing.
We can use the same trick we did in 05e9515 (upload-pack:
send keepalive packets during pack computation, 2013-09-08).
Namely, we will send an empty sideband data packet every `N`
seconds that we do not relay any stderr data over the
sideband channel. As with 05e9515, this means that we won't
bother sending keepalives when there's actual progress data,
but will kick in when it has been disabled (or if there is a
lull in the progress data).
The concept is simple, but the details are subtle enough
that they need discussing here.
Before the client sends us the pack, we don't want to do any
keepalives. We'll have sent our ref advertisement, and we're
waiting for them to send us the pack (and tell us that they
support sidebands at all).
While we're receiving the pack from the client (or waiting
for it to start), there's no need for keepalives; it's up to
them to keep the connection active by sending data.
Moreover, it would be wrong for us to do so. When we are the
server in the smart-http protocol, we must treat our
connection as half-duplex. So any keepalives we send while
receiving the pack would potentially be buffered by the
webserver. Not only does this make them useless (since they
would not be delivered in a timely manner), but it could
actually cause a deadlock if we fill up the buffer with
keepalives. (It wouldn't be wrong to send keepalives in this
phase for a full-duplex connection like ssh; it's simply
pointless, as it is the client's responsibility to speak).
As soon as we've gotten all of the pack data, then the
client is waiting for us to speak, and we should start
keepalives immediately. From here until the end of the
connection, we send one any time we are not otherwise
sending data.
But there's a catch. Receive-pack doesn't know the moment
we've gotten all the data. It passes the descriptor to
index-pack, who reads all of the data, and then starts
resolving the deltas. We have to communicate that back.
To make this work, we instruct the sideband muxer to enable
keepalives in three phases:
1. In the beginning, not at all.
2. While reading from index-pack, wait for a signal
indicating end-of-input, and then start them.
3. Afterwards, always.
The signal from index-pack in phase 2 has to come over the
stderr channel which the muxer is reading. We can't use an
extra pipe because the portable run-command interface only
gives us stderr and stdout.
Stdout is already used to pass the .keep filename back to
receive-pack. We could also send a signal there, but then we
would find out about it in the main thread. And the
keepalive needs to be done by the async muxer thread (since
it's the one writing sideband data back to the client). And
we can't reliably signal the async thread from the main
thread, because the async code sometimes uses threads and
sometimes uses forked processes.
Therefore the signal must come over the stderr channel,
where it may be interspersed with other random
human-readable messages from index-pack. This patch makes
the signal a single NUL byte. This is easy to parse, should
not appear in any normal stderr output, and we don't have to
worry about any timing issues (like seeing half the signal
bytes in one read(), and half in a subsequent one).
This is a bit ugly, but it's simple to code and should work
reliably.
Another option would be to stop using an async thread for
muxing entirely, and just poll() both stderr and stdout of
index-pack from the main thread. This would work for
index-pack (because we aren't doing anything useful in the
main thread while it runs anyway). But it would make the
connectivity check and the hook muxers much more
complicated, as they need to simultaneously feed the
sub-programs while reading their stderr.
The index-pack phase is the only one that needs this
signaling, so it could simply behave differently than the
other two. That would mean having two separate
implementations of copy_to_sideband (and the keepalive
code), though. And it still doesn't get rid of the
signaling; it just means we can write a nicer message like
"END_OF_INPUT" or something on stdout, since we don't have
to worry about separating it from the stderr cruft.
One final note: this signaling trick is only done with
index-pack, not with unpack-objects. There's no point in
doing it for the latter, because by definition it only kicks
in for a small number of objects, where keepalives are not
as useful (and this conveniently lets us avoid duplicating
the implementation).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-07-15 10:43:47 +00:00
|
|
|
} else if (!strcmp(arg, "--report-end-of-input")) {
|
|
|
|
report_end_of_input = 1;
|
2006-10-23 18:50:18 +00:00
|
|
|
} else if (!strcmp(arg, "-o")) {
|
2005-10-12 19:01:31 +00:00
|
|
|
if (index_name || (i+1) >= argc)
|
|
|
|
usage(index_pack_usage);
|
|
|
|
index_name = argv[++i];
|
2013-11-30 20:55:40 +00:00
|
|
|
} else if (starts_with(arg, "--index-version=")) {
|
2007-04-09 21:32:03 +00:00
|
|
|
char *c;
|
2011-02-25 23:43:25 +00:00
|
|
|
opts.version = strtoul(arg + 16, &c, 10);
|
|
|
|
if (opts.version > 2)
|
2012-04-23 12:30:29 +00:00
|
|
|
die(_("bad %s"), arg);
|
2007-04-09 21:32:03 +00:00
|
|
|
if (*c == ',')
|
2011-02-25 23:43:25 +00:00
|
|
|
opts.off32_limit = strtoul(c+1, &c, 0);
|
|
|
|
if (*c || opts.off32_limit & 0x80000000)
|
2012-04-23 12:30:29 +00:00
|
|
|
die(_("bad %s"), arg);
|
2016-08-24 18:41:55 +00:00
|
|
|
} else if (skip_prefix(arg, "--max-input-size=", &arg)) {
|
|
|
|
max_input_size = strtoumax(arg, NULL, 10);
|
2020-06-19 17:55:52 +00:00
|
|
|
} else if (skip_prefix(arg, "--object-format=", &arg)) {
|
|
|
|
hash_algo = hash_algo_by_name(arg);
|
|
|
|
if (hash_algo == GIT_HASH_UNKNOWN)
|
|
|
|
die(_("unknown hash algorithm '%s'"), arg);
|
|
|
|
repo_set_hash_algo(the_repository, hash_algo);
|
2021-01-25 23:37:26 +00:00
|
|
|
} else if (!strcmp(arg, "--rev-index")) {
|
|
|
|
rev_index = 1;
|
|
|
|
} else if (!strcmp(arg, "--no-rev-index")) {
|
|
|
|
rev_index = 0;
|
2005-10-12 19:01:31 +00:00
|
|
|
} else
|
|
|
|
usage(index_pack_usage);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pack_name)
|
|
|
|
usage(index_pack_usage);
|
|
|
|
pack_name = arg;
|
|
|
|
}
|
|
|
|
|
2006-10-23 18:50:18 +00:00
|
|
|
if (!pack_name && !from_stdin)
|
2005-10-12 19:01:31 +00:00
|
|
|
usage(index_pack_usage);
|
2006-10-26 03:28:17 +00:00
|
|
|
if (fix_thin_pack && !from_stdin)
|
2022-01-05 20:02:19 +00:00
|
|
|
die(_("the option '%s' requires '%s'"), "--fix-thin", "--stdin");
|
2016-12-16 02:30:59 +00:00
|
|
|
if (from_stdin && !startup_info->have_repository)
|
|
|
|
die(_("--stdin requires a git repository"));
|
2020-06-19 17:55:52 +00:00
|
|
|
if (from_stdin && hash_algo)
|
2022-01-05 20:02:16 +00:00
|
|
|
die(_("options '%s' and '%s' cannot be used together"), "--object-format", "--stdin");
|
2016-03-03 19:29:09 +00:00
|
|
|
if (!index_name && pack_name)
|
2021-01-25 23:37:22 +00:00
|
|
|
index_name = derive_filename(pack_name, "pack", "idx", &index_name_buf);
|
2016-03-03 19:29:09 +00:00
|
|
|
|
2021-01-25 23:37:26 +00:00
|
|
|
opts.flags &= ~(WRITE_REV | WRITE_REV_VERIFY);
|
|
|
|
if (rev_index) {
|
|
|
|
opts.flags |= verify ? WRITE_REV_VERIFY : WRITE_REV;
|
|
|
|
if (index_name)
|
|
|
|
rev_index_name = derive_filename(index_name,
|
|
|
|
"idx", "rev",
|
|
|
|
&rev_index_name_buf);
|
|
|
|
}
|
|
|
|
|
2011-02-03 01:29:01 +00:00
|
|
|
if (verify) {
|
|
|
|
if (!index_name)
|
2012-04-23 12:30:29 +00:00
|
|
|
die(_("--verify with no packfile name given"));
|
2011-02-03 01:29:01 +00:00
|
|
|
read_idx_option(&opts, index_name);
|
2011-11-17 06:04:13 +00:00
|
|
|
opts.flags |= WRITE_IDX_VERIFY | WRITE_IDX_STRICT;
|
2011-02-03 01:29:01 +00:00
|
|
|
}
|
2011-11-17 06:04:13 +00:00
|
|
|
if (strict)
|
|
|
|
opts.flags |= WRITE_IDX_STRICT;
|
2005-10-12 19:01:31 +00:00
|
|
|
|
2018-11-03 08:48:40 +00:00
|
|
|
if (HAVE_THREADS && !nr_threads) {
|
2012-05-06 12:31:55 +00:00
|
|
|
nr_threads = online_cpus();
|
index-pack: adjust default threading cap
Commit b8a2486f15 (index-pack: support multithreaded delta resolving,
2012-05-06) describes an experiment that shows that setting the number
of threads for index-pack higher than 3 does not help.
I repeated that experiment using a more modern version of Git and a more
modern CPU and got different results.
Here are timings for p5302 against linux.git run on my laptop, a Core
i9-9880H with 8 cores plus hyperthreading (so online-cpus returns 16):
5302.3: index-pack 0 threads 256.28(253.41+2.79)
5302.4: index-pack 1 threads 257.03(254.03+2.91)
5302.5: index-pack 2 threads 149.39(268.34+3.06)
5302.6: index-pack 4 threads 94.96(294.10+3.23)
5302.7: index-pack 8 threads 68.12(339.26+3.89)
5302.8: index-pack 16 threads 70.90(655.03+7.21)
5302.9: index-pack default number of threads 116.91(290.05+3.21)
You can see that wall-clock times continue to improve dramatically up to
the number of cores, but bumping beyond that (into hyperthreading
territory) does not help (and in fact hurts a little).
Here's the same experiment on a machine with dual Xeon 6230's, totaling
40 cores (80 with hyperthreading):
5302.3: index-pack 0 threads 310.04(302.73+6.90)
5302.4: index-pack 1 threads 310.55(302.68+7.40)
5302.5: index-pack 2 threads 178.17(304.89+8.20)
5302.6: index-pack 5 threads 99.53(315.54+9.56)
5302.7: index-pack 10 threads 72.80(327.37+12.79)
5302.8: index-pack 20 threads 60.68(357.74+21.66)
5302.9: index-pack 40 threads 58.07(454.44+67.96)
5302.10: index-pack 80 threads 59.81(720.45+334.52)
5302.11: index-pack default number of threads 134.18(309.32+7.98)
The results are similar; things stop improving at 40 threads. Curiously,
going from 20 to 40 really doesn't help much, either (and increases CPU
time considerably). So that may represent an actual barrier to
parallelism, where we lose out due to context-switching and loss of
cache locality, but don't reap the wall-clock benefits due to contention
of our coarse-grained locks.
So what's a good default value? It's clear that the current cap of 3 is
too low; our default values are 42% and 57% slower than the best times
on each machine. The results on the 40-core machine imply that 20
threads is an actual barrier regardless of the number of cores, so we'll
take that as a maximum. We get the best results on these machines at
half of the online-cpus value. That's presumably a result of the
hyperthreading. That's common on multi-core Intel processors, but not
necessarily elsewhere. But if we take it as an assumption, we can
perform optimally on hyperthreaded machines and still do much better
than the status quo on other machines, as long as we never half below
the current value of 3.
So that's what this patch does.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-08-21 17:58:00 +00:00
|
|
|
/*
|
|
|
|
* Experiments show that going above 20 threads doesn't help,
|
|
|
|
* no matter how many cores you have. Below that, we tend to
|
|
|
|
* max at half the number of online_cpus(), presumably because
|
|
|
|
* half of those are hyperthreads rather than full cores. We'll
|
|
|
|
* never reduce the level below "3", though, to match a
|
|
|
|
* historical value that nobody complained about.
|
|
|
|
*/
|
|
|
|
if (nr_threads < 4)
|
|
|
|
; /* too few cores to consider capping */
|
|
|
|
else if (nr_threads < 6)
|
|
|
|
nr_threads = 3; /* historic cap */
|
|
|
|
else if (nr_threads < 40)
|
|
|
|
nr_threads /= 2;
|
|
|
|
else
|
|
|
|
nr_threads = 20; /* hard cap */
|
2012-05-06 12:31:55 +00:00
|
|
|
}
|
|
|
|
|
2006-10-23 18:50:18 +00:00
|
|
|
curr_pack = open_pack_file(pack_name);
|
2005-10-12 19:01:31 +00:00
|
|
|
parse_pack_header();
|
2021-03-13 16:17:22 +00:00
|
|
|
CALLOC_ARRAY(objects, st_add(nr_objects, 1));
|
2015-02-26 10:52:07 +00:00
|
|
|
if (show_stat)
|
2021-03-13 16:17:22 +00:00
|
|
|
CALLOC_ARRAY(obj_stat, st_add(nr_objects, 1));
|
|
|
|
CALLOC_ARRAY(ofs_deltas, nr_objects);
|
2018-02-01 02:18:39 +00:00
|
|
|
parse_pack_objects(pack_hash);
|
receive-pack: send keepalives during quiet periods
After a client has sent us the complete pack, we may spend
some time processing the data and running hooks. If the
client asked us to be quiet, receive-pack won't send any
progress data during the index-pack or connectivity-check
steps. And hooks may or may not produce their own progress
output. In these cases, the network connection is totally
silent from both ends.
Git itself doesn't care about this (it will wait forever),
but other parts of the system (e.g., firewalls,
load-balancers, etc) might hang up the connection. So we'd
like to send some sort of keepalive to let the network and
the client side know that we're still alive and processing.
We can use the same trick we did in 05e9515 (upload-pack:
send keepalive packets during pack computation, 2013-09-08).
Namely, we will send an empty sideband data packet every `N`
seconds that we do not relay any stderr data over the
sideband channel. As with 05e9515, this means that we won't
bother sending keepalives when there's actual progress data,
but will kick in when it has been disabled (or if there is a
lull in the progress data).
The concept is simple, but the details are subtle enough
that they need discussing here.
Before the client sends us the pack, we don't want to do any
keepalives. We'll have sent our ref advertisement, and we're
waiting for them to send us the pack (and tell us that they
support sidebands at all).
While we're receiving the pack from the client (or waiting
for it to start), there's no need for keepalives; it's up to
them to keep the connection active by sending data.
Moreover, it would be wrong for us to do so. When we are the
server in the smart-http protocol, we must treat our
connection as half-duplex. So any keepalives we send while
receiving the pack would potentially be buffered by the
webserver. Not only does this make them useless (since they
would not be delivered in a timely manner), but it could
actually cause a deadlock if we fill up the buffer with
keepalives. (It wouldn't be wrong to send keepalives in this
phase for a full-duplex connection like ssh; it's simply
pointless, as it is the client's responsibility to speak).
As soon as we've gotten all of the pack data, then the
client is waiting for us to speak, and we should start
keepalives immediately. From here until the end of the
connection, we send one any time we are not otherwise
sending data.
But there's a catch. Receive-pack doesn't know the moment
we've gotten all the data. It passes the descriptor to
index-pack, who reads all of the data, and then starts
resolving the deltas. We have to communicate that back.
To make this work, we instruct the sideband muxer to enable
keepalives in three phases:
1. In the beginning, not at all.
2. While reading from index-pack, wait for a signal
indicating end-of-input, and then start them.
3. Afterwards, always.
The signal from index-pack in phase 2 has to come over the
stderr channel which the muxer is reading. We can't use an
extra pipe because the portable run-command interface only
gives us stderr and stdout.
Stdout is already used to pass the .keep filename back to
receive-pack. We could also send a signal there, but then we
would find out about it in the main thread. And the
keepalive needs to be done by the async muxer thread (since
it's the one writing sideband data back to the client). And
we can't reliably signal the async thread from the main
thread, because the async code sometimes uses threads and
sometimes uses forked processes.
Therefore the signal must come over the stderr channel,
where it may be interspersed with other random
human-readable messages from index-pack. This patch makes
the signal a single NUL byte. This is easy to parse, should
not appear in any normal stderr output, and we don't have to
worry about any timing issues (like seeing half the signal
bytes in one read(), and half in a subsequent one).
This is a bit ugly, but it's simple to code and should work
reliably.
Another option would be to stop using an async thread for
muxing entirely, and just poll() both stderr and stdout of
index-pack from the main thread. This would work for
index-pack (because we aren't doing anything useful in the
main thread while it runs anyway). But it would make the
connectivity check and the hook muxers much more
complicated, as they need to simultaneously feed the
sub-programs while reading their stderr.
The index-pack phase is the only one that needs this
signaling, so it could simply behave differently than the
other two. That would mean having two separate
implementations of copy_to_sideband (and the keepalive
code), though. And it still doesn't get rid of the
signaling; it just means we can write a nicer message like
"END_OF_INPUT" or something on stdout, since we don't have
to worry about separating it from the stderr cruft.
One final note: this signaling trick is only done with
index-pack, not with unpack-objects. There's no point in
doing it for the latter, because by definition it only kicks
in for a small number of objects, where keepalives are not
as useful (and this conveniently lets us avoid duplicating
the implementation).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-07-15 10:43:47 +00:00
|
|
|
if (report_end_of_input)
|
|
|
|
write_in_full(2, "\0", 1);
|
2012-05-06 12:31:54 +00:00
|
|
|
resolve_deltas();
|
2018-02-01 02:18:39 +00:00
|
|
|
conclude_pack(fix_thin_pack, curr_pack, pack_hash);
|
index-pack: kill union delta_base to save memory
Once we know the number of objects in the input pack, we allocate an
array of nr_objects of struct delta_entry. On x86-64, this struct is
32 bytes long. The union delta_base, which is part of struct
delta_entry, provides enough space to store either ofs-delta (8 bytes)
or ref-delta (20 bytes).
Because ofs-delta encoding is more efficient space-wise and more
performant at runtime than ref-delta encoding, Git packers try to use
ofs-delta whenever possible, and it is expected that objects encoded
as ref-delta are minority.
In the best clone case where no ref-delta object is present, we waste
(20-8) * nr_objects bytes because of this union. That's about 38MB out
of 100MB for deltas[] with 3.4M objects, or 38%. deltas[] would be
around 62MB without the waste.
This patch attempts to eliminate that. deltas[] array is split into
two: one for ofs-delta and one for ref-delta. Many functions are also
duplicated because of this split. With this patch, ofs_deltas[] array
takes 51MB. ref_deltas[] should remain unallocated in clone case (0
bytes). This array grows as we see ref-delta. We save about half in
this case, or 25% of total bookkeeping.
The saving is more than the calculation above because some padding in
the old delta_entry struct is removed. ofs_delta_entry is 16 bytes,
including the 4 bytes padding. That's 13MB for padding, but packing
the struct could break platforms that do not support unaligned
access. If someone on 32-bit is really low on memory and only deals
with packs smaller than 2G, using 32-bit off_t would eliminate the
padding and save 27MB on top.
A note about ofs_deltas allocation. We could use ref_deltas memory
allocation strategy for ofs_deltas. But that probably just adds more
overhead on top. ofs-deltas are generally the majority (1/2 to 2/3) in
any pack. Incremental realloc may lead to too many memcpy. And if we
preallocate, say 1/2 or 2/3 of nr_objects initially, the growth rate
of ALLOC_GROW() could make this array larger than nr_objects, wasting
more memory.
Brought-up-by: Matthew Sporleder <msporleder@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-18 10:47:05 +00:00
|
|
|
free(ofs_deltas);
|
|
|
|
free(ref_deltas);
|
2008-02-25 21:46:12 +00:00
|
|
|
if (strict)
|
2013-05-26 01:16:17 +00:00
|
|
|
foreign_nr = check_objects();
|
2007-06-01 19:18:05 +00:00
|
|
|
|
2013-03-19 13:01:15 +00:00
|
|
|
if (show_stat)
|
2011-06-03 22:32:15 +00:00
|
|
|
show_pack_info(stat_only);
|
|
|
|
|
2016-02-22 22:44:25 +00:00
|
|
|
ALLOC_ARRAY(idx_objects, nr_objects);
|
2007-06-01 19:18:05 +00:00
|
|
|
for (i = 0; i < nr_objects; i++)
|
|
|
|
idx_objects[i] = &objects[i].idx;
|
2018-02-01 02:18:39 +00:00
|
|
|
curr_index = write_idx_file(index_name, idx_objects, nr_objects, &opts, pack_hash);
|
2021-01-25 23:37:26 +00:00
|
|
|
if (rev_index)
|
|
|
|
curr_rev_index = write_rev_file(rev_index_name, idx_objects,
|
|
|
|
nr_objects, pack_hash,
|
|
|
|
opts.flags);
|
2007-06-01 19:18:05 +00:00
|
|
|
free(idx_objects);
|
|
|
|
|
2011-02-03 01:29:01 +00:00
|
|
|
if (!verify)
|
|
|
|
final(pack_name, curr_pack,
|
|
|
|
index_name, curr_index,
|
2021-01-25 23:37:26 +00:00
|
|
|
rev_index_name, curr_rev_index,
|
introduce fetch-object: fetch one promisor object
Introduce fetch-object, providing the ability to fetch one object from a
promisor remote.
This uses fetch-pack. To do this, the transport mechanism has been
updated with 2 flags, "from-promisor" to indicate that the resulting
pack comes from a promisor remote (and thus should be annotated as such
by index-pack), and "no-dependents" to indicate that only the objects
themselves need to be fetched (but fetching additional objects is
nevertheless safe).
Whenever "no-dependents" is used, fetch-pack will refrain from using any
object flags, because it is most likely invoked as part of a dynamic
object fetch by another Git command (which may itself use object flags).
An alternative to this is to leave fetch-pack alone, and instead update
the allocation of flags so that fetch-pack's flags never overlap with
any others, but this will end up shrinking the number of flags available
to nearly every other Git command (that is, every Git command that
accesses objects), so the approach in this commit was used instead.
This will be tested in a subsequent commit.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-12-05 16:58:49 +00:00
|
|
|
keep_msg, promisor_msg,
|
2018-02-01 02:18:39 +00:00
|
|
|
pack_hash);
|
2011-02-03 01:29:01 +00:00
|
|
|
else
|
|
|
|
close(input_fd);
|
index-pack: check .gitmodules files with --strict
Now that the internal fsck code has all of the plumbing we
need, we can start checking incoming .gitmodules files.
Naively, it seems like we would just need to add a call to
fsck_finish() after we've processed all of the objects. And
that would be enough to cover the initial test included
here. But there are two extra bits:
1. We currently don't bother calling fsck_object() at all
for blobs, since it has traditionally been a noop. We'd
actually catch these blobs in fsck_finish() at the end,
but it's more efficient to check them when we already
have the object loaded in memory.
2. The second pass done by fsck_finish() needs to access
the objects, but we're actually indexing the pack in
this process. In theory we could give the fsck code a
special callback for accessing the in-pack data, but
it's actually quite tricky:
a. We don't have an internal efficient index mapping
oids to packfile offsets. We only generate it on
the fly as part of writing out the .idx file.
b. We'd still have to reconstruct deltas, which means
we'd basically have to replicate all of the
reading logic in packfile.c.
Instead, let's avoid running fsck_finish() until after
we've written out the .idx file, and then just add it
to our internal packed_git list.
This does mean that the objects are "in the repository"
before we finish our fsck checks. But unpack-objects
already exhibits this same behavior, and it's an
acceptable tradeoff here for the same reason: the
quarantine mechanism means that pushes will be
fully protected.
In addition to a basic push test in t7415, we add a sneaky
pack that reverses the usual object order in the pack,
requiring that index-pack access the tree and blob during
the "finish" step.
This already works for unpack-objects (since it will have
written out loose objects), but we'll check it with this
sneaky pack for good measure.
Signed-off-by: Jeff King <peff@peff.net>
2018-05-04 23:45:01 +00:00
|
|
|
|
2021-03-28 13:15:49 +00:00
|
|
|
if (do_fsck_object && fsck_finish(&fsck_options))
|
|
|
|
die(_("fsck error in pack objects"));
|
index-pack: check .gitmodules files with --strict
Now that the internal fsck code has all of the plumbing we
need, we can start checking incoming .gitmodules files.
Naively, it seems like we would just need to add a call to
fsck_finish() after we've processed all of the objects. And
that would be enough to cover the initial test included
here. But there are two extra bits:
1. We currently don't bother calling fsck_object() at all
for blobs, since it has traditionally been a noop. We'd
actually catch these blobs in fsck_finish() at the end,
but it's more efficient to check them when we already
have the object loaded in memory.
2. The second pass done by fsck_finish() needs to access
the objects, but we're actually indexing the pack in
this process. In theory we could give the fsck code a
special callback for accessing the in-pack data, but
it's actually quite tricky:
a. We don't have an internal efficient index mapping
oids to packfile offsets. We only generate it on
the fly as part of writing out the .idx file.
b. We'd still have to reconstruct deltas, which means
we'd basically have to replicate all of the
reading logic in packfile.c.
Instead, let's avoid running fsck_finish() until after
we've written out the .idx file, and then just add it
to our internal packed_git list.
This does mean that the objects are "in the repository"
before we finish our fsck checks. But unpack-objects
already exhibits this same behavior, and it's an
acceptable tradeoff here for the same reason: the
quarantine mechanism means that pushes will be
fully protected.
In addition to a basic push test in t7415, we add a sneaky
pack that reverses the usual object order in the pack,
requiring that index-pack access the tree and blob during
the "finish" step.
This already works for unpack-objects (since it will have
written out loose objects), but we'll check it with this
sneaky pack for good measure.
Signed-off-by: Jeff King <peff@peff.net>
2018-05-04 23:45:01 +00:00
|
|
|
|
index-pack: fix memory leaks
Fix various memory leaks in "git index-pack", due to how tightly
coupled this command is with the revision walking this doesn't make
any new tests pass.
But e.g. this now passes, and had several failures before, i.e. we
still have failures in tests 3, 5 etc., which are being skipped here.
./t5300-pack-object.sh --run=1-2,4,6-27,30-42
It is a bit odd that we'll free "opts.anomaly", since the "opts" is a
"struct pack_idx_option" declared in pack.h. In pack-write.c there's a
reset_pack_idx_option(), but it only wipes the contents, but doesn't
free() anything.
Doing this here in cmd_index_pack() is correct because while the
struct is declared in pack.h, this code in builtin/index-pack.c (in
read_v2_anomalous_offsets()) is what allocates the "opts.anomaly", so
we should also free it here.
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-03-04 18:32:04 +00:00
|
|
|
free(opts.anomaly);
|
2005-10-12 19:01:31 +00:00
|
|
|
free(objects);
|
2014-06-30 16:59:10 +00:00
|
|
|
strbuf_release(&index_name_buf);
|
2021-01-25 23:37:26 +00:00
|
|
|
strbuf_release(&rev_index_name_buf);
|
2022-05-02 16:50:37 +00:00
|
|
|
if (!pack_name)
|
2010-01-22 15:55:19 +00:00
|
|
|
free((void *) curr_pack);
|
2022-05-02 16:50:37 +00:00
|
|
|
if (!index_name)
|
2010-01-22 15:55:19 +00:00
|
|
|
free((void *) curr_index);
|
2022-05-02 16:50:37 +00:00
|
|
|
if (!rev_index_name)
|
2021-01-25 23:37:26 +00:00
|
|
|
free((void *) curr_rev_index);
|
2005-10-12 19:01:31 +00:00
|
|
|
|
2013-05-26 01:16:17 +00:00
|
|
|
/*
|
|
|
|
* Let the caller know this pack is not self contained
|
|
|
|
*/
|
|
|
|
if (check_self_contained_and_connected && foreign_nr)
|
|
|
|
return 1;
|
|
|
|
|
2005-10-12 19:01:31 +00:00
|
|
|
return 0;
|
|
|
|
}
|