Skip to content

Commit f4d91a4

Browse files
committed
Merge branch 'nd/pack-deltify-regression-fix' into pu
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 aea1667 + 9ac3f0e commit f4d91a4

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
@@ -2029,10 +2029,6 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
20292029
delta_buf = create_delta(src->index, trg->data, trg_size, &delta_size, max_size);
20302030
if (!delta_buf)
20312031
return 0;
2032-
if (delta_size >= (1U << OE_DELTA_SIZE_BITS)) {
2033-
free(delta_buf);
2034-
return 0;
2035-
}
20362032

20372033
if (DELTA(trg_entry)) {
20382034
/* Prefer only shallower same-sized deltas. */
@@ -2284,6 +2280,7 @@ static void init_threaded_search(void)
22842280
pthread_mutex_init(&cache_mutex, NULL);
22852281
pthread_mutex_init(&progress_mutex, NULL);
22862282
pthread_cond_init(&progress_cond, NULL);
2283+
pthread_mutex_init(&to_pack.lock, NULL);
22872284
old_try_to_free_routine = set_try_to_free_routine(try_to_free_from_threads);
22882285
}
22892286

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

67
#define DEFAULT_DELTA_CACHE_SIZE (256 * 1024 * 1024)
78

@@ -14,7 +15,7 @@
1415
* above this limit. Don't lower it too much.
1516
*/
1617
#define OE_SIZE_BITS 31
17-
#define OE_DELTA_SIZE_BITS 20
18+
#define OE_DELTA_SIZE_BITS 23
1819

1920
/*
2021
* State flags for depth-first search used for analyzing delta cycles.
@@ -94,11 +95,12 @@ struct object_entry {
9495
*/
9596
unsigned delta_size_:OE_DELTA_SIZE_BITS; /* delta data size (uncompressed) */
9697
unsigned delta_size_valid:1;
98+
unsigned char in_pack_header_size;
9799
unsigned in_pack_idx:OE_IN_PACK_BITS; /* already in pack */
98100
unsigned z_delta_size:OE_Z_DELTA_BITS;
99101
unsigned type_valid:1;
100-
unsigned type_:TYPE_BITS;
101102
unsigned no_try_delta:1;
103+
unsigned type_:TYPE_BITS;
102104
unsigned in_pack_type:TYPE_BITS; /* could be delta */
103105
unsigned preferred_base:1; /*
104106
* we do not pack this, but is available
@@ -108,17 +110,16 @@ struct object_entry {
108110
unsigned tagged:1; /* near the very tip of refs */
109111
unsigned filled:1; /* assigned write-order */
110112
unsigned dfs_state:OE_DFS_STATE_BITS;
111-
unsigned char in_pack_header_size;
112113
unsigned depth:OE_DEPTH_BITS;
113114

114115
/*
115116
* pahole results on 64-bit linux (gcc and clang)
116117
*
117-
* size: 80, bit_padding: 20 bits, holes: 8 bits
118+
* size: 80, bit_padding: 9 bits
118119
*
119120
* and on 32-bit (gcc)
120121
*
121-
* size: 76, bit_padding: 20 bits, holes: 8 bits
122+
* size: 76, bit_padding: 9 bits
122123
*/
123124
};
124125

@@ -130,6 +131,7 @@ struct packing_data {
130131
uint32_t index_size;
131132

132133
unsigned int *in_pack_pos;
134+
unsigned long *delta_size;
133135

134136
/*
135137
* Only one of these can be non-NULL and they have different
@@ -140,10 +142,29 @@ struct packing_data {
140142
struct packed_git **in_pack_by_idx;
141143
struct packed_git **in_pack;
142144

145+
#ifndef NO_PTHREADS
146+
pthread_mutex_t lock;
147+
#endif
148+
143149
uintmax_t oe_size_limit;
150+
uintmax_t oe_delta_size_limit;
144151
};
145152

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

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

349386
#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)