Skip to content

Commit 29d9e3e

Browse files
committed
Merge branch 'nd/pack-deltify-regression-fix'
In a recent update in 2.18 era, "git pack-objects" started producing a larger than necessary packfiles by missing opportunities to use large deltas. * nd/pack-deltify-regression-fix: pack-objects: fix performance issues on packing large deltas
2 parents 7e8bfb0 + 9ac3f0e commit 29d9e3e

File tree

5 files changed

+58
-15
lines changed

5 files changed

+58
-15
lines changed

builtin/pack-objects.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2041,10 +2041,6 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
20412041
delta_buf = create_delta(src->index, trg->data, trg_size, &delta_size, max_size);
20422042
if (!delta_buf)
20432043
return 0;
2044-
if (delta_size >= (1U << OE_DELTA_SIZE_BITS)) {
2045-
free(delta_buf);
2046-
return 0;
2047-
}
20482044

20492045
if (DELTA(trg_entry)) {
20502046
/* Prefer only shallower same-sized deltas. */
@@ -2303,6 +2299,7 @@ static void init_threaded_search(void)
23032299
pthread_mutex_init(&cache_mutex, NULL);
23042300
pthread_mutex_init(&progress_mutex, NULL);
23052301
pthread_cond_init(&progress_cond, NULL);
2302+
pthread_mutex_init(&to_pack.lock, NULL);
23062303
old_try_to_free_routine = set_try_to_free_routine(try_to_free_from_threads);
23072304
}
23082305

ci/run-build-and-tests.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ then
1414
export GIT_TEST_SPLIT_INDEX=yes
1515
export GIT_TEST_FULL_IN_PACK_ARRAY=true
1616
export GIT_TEST_OE_SIZE=10
17+
export GIT_TEST_OE_DELTA_SIZE=5
1718
make --quiet test
1819
fi
1920

pack-objects.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ void prepare_packing_data(struct packing_data *pdata)
146146

147147
pdata->oe_size_limit = git_env_ulong("GIT_TEST_OE_SIZE",
148148
1U << OE_SIZE_BITS);
149+
pdata->oe_delta_size_limit = git_env_ulong("GIT_TEST_OE_DELTA_SIZE",
150+
1UL << OE_DELTA_SIZE_BITS);
149151
}
150152

151153
struct object_entry *packlist_alloc(struct packing_data *pdata,
@@ -160,6 +162,8 @@ struct object_entry *packlist_alloc(struct packing_data *pdata,
160162

161163
if (!pdata->in_pack_by_idx)
162164
REALLOC_ARRAY(pdata->in_pack, pdata->nr_alloc);
165+
if (pdata->delta_size)
166+
REALLOC_ARRAY(pdata->delta_size, pdata->nr_alloc);
163167
}
164168

165169
new_entry = pdata->objects + pdata->nr_objects++;

pack-objects.h

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define PACK_OBJECTS_H
33

44
#include "object-store.h"
5+
#include "thread-utils.h"
56
#include "pack.h"
67

78
#define DEFAULT_DELTA_CACHE_SIZE (256 * 1024 * 1024)
@@ -15,7 +16,7 @@
1516
* above this limit. Don't lower it too much.
1617
*/
1718
#define OE_SIZE_BITS 31
18-
#define OE_DELTA_SIZE_BITS 20
19+
#define OE_DELTA_SIZE_BITS 23
1920

2021
/*
2122
* State flags for depth-first search used for analyzing delta cycles.
@@ -95,11 +96,12 @@ struct object_entry {
9596
*/
9697
unsigned delta_size_:OE_DELTA_SIZE_BITS; /* delta data size (uncompressed) */
9798
unsigned delta_size_valid:1;
99+
unsigned char in_pack_header_size;
98100
unsigned in_pack_idx:OE_IN_PACK_BITS; /* already in pack */
99101
unsigned z_delta_size:OE_Z_DELTA_BITS;
100102
unsigned type_valid:1;
101-
unsigned type_:TYPE_BITS;
102103
unsigned no_try_delta:1;
104+
unsigned type_:TYPE_BITS;
103105
unsigned in_pack_type:TYPE_BITS; /* could be delta */
104106
unsigned preferred_base:1; /*
105107
* we do not pack this, but is available
@@ -109,17 +111,16 @@ struct object_entry {
109111
unsigned tagged:1; /* near the very tip of refs */
110112
unsigned filled:1; /* assigned write-order */
111113
unsigned dfs_state:OE_DFS_STATE_BITS;
112-
unsigned char in_pack_header_size;
113114
unsigned depth:OE_DEPTH_BITS;
114115

115116
/*
116117
* pahole results on 64-bit linux (gcc and clang)
117118
*
118-
* size: 80, bit_padding: 20 bits, holes: 8 bits
119+
* size: 80, bit_padding: 9 bits
119120
*
120121
* and on 32-bit (gcc)
121122
*
122-
* size: 76, bit_padding: 20 bits, holes: 8 bits
123+
* size: 76, bit_padding: 9 bits
123124
*/
124125
};
125126

@@ -131,6 +132,7 @@ struct packing_data {
131132
uint32_t index_size;
132133

133134
unsigned int *in_pack_pos;
135+
unsigned long *delta_size;
134136

135137
/*
136138
* Only one of these can be non-NULL and they have different
@@ -141,10 +143,29 @@ struct packing_data {
141143
struct packed_git **in_pack_by_idx;
142144
struct packed_git **in_pack;
143145

146+
#ifndef NO_PTHREADS
147+
pthread_mutex_t lock;
148+
#endif
149+
144150
uintmax_t oe_size_limit;
151+
uintmax_t oe_delta_size_limit;
145152
};
146153

147154
void prepare_packing_data(struct packing_data *pdata);
155+
156+
static inline void packing_data_lock(struct packing_data *pdata)
157+
{
158+
#ifndef NO_PTHREADS
159+
pthread_mutex_lock(&pdata->lock);
160+
#endif
161+
}
162+
static inline void packing_data_unlock(struct packing_data *pdata)
163+
{
164+
#ifndef NO_PTHREADS
165+
pthread_mutex_unlock(&pdata->lock);
166+
#endif
167+
}
168+
148169
struct object_entry *packlist_alloc(struct packing_data *pdata,
149170
const unsigned char *sha1,
150171
uint32_t index_pos);
@@ -333,18 +354,34 @@ static inline unsigned long oe_delta_size(struct packing_data *pack,
333354
{
334355
if (e->delta_size_valid)
335356
return e->delta_size_;
336-
return oe_size(pack, e);
357+
358+
/*
359+
* pack->detla_size[] can't be NULL because oe_set_delta_size()
360+
* must have been called when a new delta is saved with
361+
* oe_set_delta().
362+
* If oe_delta() returns NULL (i.e. default state, which means
363+
* delta_size_valid is also false), then the caller must never
364+
* call oe_delta_size().
365+
*/
366+
return pack->delta_size[e - pack->objects];
337367
}
338368

339369
static inline void oe_set_delta_size(struct packing_data *pack,
340370
struct object_entry *e,
341371
unsigned long size)
342372
{
343-
e->delta_size_ = size;
344-
e->delta_size_valid = e->delta_size_ == size;
345-
if (!e->delta_size_valid && size != oe_size(pack, e))
346-
BUG("this can only happen in check_object() "
347-
"where delta size is the same as entry size");
373+
if (size < pack->oe_delta_size_limit) {
374+
e->delta_size_ = size;
375+
e->delta_size_valid = 1;
376+
} else {
377+
packing_data_lock(pack);
378+
if (!pack->delta_size)
379+
ALLOC_ARRAY(pack->delta_size, pack->nr_alloc);
380+
packing_data_unlock(pack);
381+
382+
pack->delta_size[e - pack->objects] = size;
383+
e->delta_size_valid = 0;
384+
}
348385
}
349386

350387
#endif

t/README

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,10 @@ packs on demand. This normally only happens when the object size is
315315
over 2GB. This variable forces the code path on any object larger than
316316
<n> bytes.
317317

318+
GIT_TEST_OE_DELTA_SIZE=<n> exercises the uncomon pack-objects code
319+
path where deltas larger than this limit require extra memory
320+
allocation for bookkeeping.
321+
318322
Naming Tests
319323
------------
320324

0 commit comments

Comments
 (0)