Skip to content

Commit ae533c4

Browse files
committed
Merge branch 'jm/cache-entry-from-mem-pool'
For a large tree, the index needs to hold many cache entries allocated on heap. These cache entries are now allocated out of a dedicated memory pool to amortize malloc(3) overhead. * jm/cache-entry-from-mem-pool: block alloc: add validations around cache_entry lifecyle block alloc: allocate cache entries from mem_pool mem-pool: fill out functionality mem-pool: add life cycle management functions mem-pool: only search head block for available space block alloc: add lifecycle APIs for cache_entry structs read-cache: teach make_cache_entry to take object_id read-cache: teach refresh_cache_entry to take istate
2 parents 30bf8d9 + 8616a2d commit ae533c4

16 files changed

+515
-134
lines changed

apply.c

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4093,12 +4093,12 @@ static int build_fake_ancestor(struct apply_state *state, struct patch *list)
40934093
return error(_("sha1 information is lacking or useless "
40944094
"(%s)."), name);
40954095

4096-
ce = make_cache_entry(patch->old_mode, oid.hash, name, 0, 0);
4096+
ce = make_cache_entry(&result, patch->old_mode, &oid, name, 0, 0);
40974097
if (!ce)
40984098
return error(_("make_cache_entry failed for path '%s'"),
40994099
name);
41004100
if (add_index_entry(&result, ce, ADD_CACHE_OK_TO_ADD)) {
4101-
free(ce);
4101+
discard_cache_entry(ce);
41024102
return error(_("could not add %s to temporary index"),
41034103
name);
41044104
}
@@ -4266,9 +4266,8 @@ static int add_index_file(struct apply_state *state,
42664266
struct stat st;
42674267
struct cache_entry *ce;
42684268
int namelen = strlen(path);
4269-
unsigned ce_size = cache_entry_size(namelen);
42704269

4271-
ce = xcalloc(1, ce_size);
4270+
ce = make_empty_cache_entry(&the_index, namelen);
42724271
memcpy(ce->name, path, namelen);
42734272
ce->ce_mode = create_ce_mode(mode);
42744273
ce->ce_flags = create_ce_flags(0);
@@ -4281,27 +4280,27 @@ static int add_index_file(struct apply_state *state,
42814280

42824281
if (!skip_prefix(buf, "Subproject commit ", &s) ||
42834282
get_oid_hex(s, &ce->oid)) {
4284-
free(ce);
4285-
return error(_("corrupt patch for submodule %s"), path);
4283+
discard_cache_entry(ce);
4284+
return error(_("corrupt patch for submodule %s"), path);
42864285
}
42874286
} else {
42884287
if (!state->cached) {
42894288
if (lstat(path, &st) < 0) {
4290-
free(ce);
4289+
discard_cache_entry(ce);
42914290
return error_errno(_("unable to stat newly "
42924291
"created file '%s'"),
42934292
path);
42944293
}
42954294
fill_stat_cache_info(ce, &st);
42964295
}
42974296
if (write_object_file(buf, size, blob_type, &ce->oid) < 0) {
4298-
free(ce);
4297+
discard_cache_entry(ce);
42994298
return error(_("unable to create backing store "
43004299
"for newly created file %s"), path);
43014300
}
43024301
}
43034302
if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0) {
4304-
free(ce);
4303+
discard_cache_entry(ce);
43054304
return error(_("unable to add cache entry for %s"), path);
43064305
}
43074306

@@ -4425,27 +4424,26 @@ static int add_conflicted_stages_file(struct apply_state *state,
44254424
struct patch *patch)
44264425
{
44274426
int stage, namelen;
4428-
unsigned ce_size, mode;
4427+
unsigned mode;
44294428
struct cache_entry *ce;
44304429

44314430
if (!state->update_index)
44324431
return 0;
44334432
namelen = strlen(patch->new_name);
4434-
ce_size = cache_entry_size(namelen);
44354433
mode = patch->new_mode ? patch->new_mode : (S_IFREG | 0644);
44364434

44374435
remove_file_from_cache(patch->new_name);
44384436
for (stage = 1; stage < 4; stage++) {
44394437
if (is_null_oid(&patch->threeway_stage[stage - 1]))
44404438
continue;
4441-
ce = xcalloc(1, ce_size);
4439+
ce = make_empty_cache_entry(&the_index, namelen);
44424440
memcpy(ce->name, patch->new_name, namelen);
44434441
ce->ce_mode = create_ce_mode(mode);
44444442
ce->ce_flags = create_ce_flags(stage);
44454443
ce->ce_namelen = namelen;
44464444
oidcpy(&ce->oid, &patch->threeway_stage[stage - 1]);
44474445
if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0) {
4448-
free(ce);
4446+
discard_cache_entry(ce);
44494447
return error(_("unable to add cache entry for %s"),
44504448
patch->new_name);
44514449
}

blame.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
176176
struct strbuf buf = STRBUF_INIT;
177177
const char *ident;
178178
time_t now;
179-
int size, len;
179+
int len;
180180
struct cache_entry *ce;
181181
unsigned mode;
182182
struct strbuf msg = STRBUF_INIT;
@@ -274,8 +274,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
274274
/* Let's not bother reading from HEAD tree */
275275
mode = S_IFREG | 0644;
276276
}
277-
size = cache_entry_size(len);
278-
ce = xcalloc(1, size);
277+
ce = make_empty_cache_entry(&the_index, len);
279278
oidcpy(&ce->oid, &origin->blob_oid);
280279
memcpy(ce->name, path, len);
281280
ce->ce_flags = create_ce_flags(0);

builtin/checkout.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
7979
return READ_TREE_RECURSIVE;
8080

8181
len = base->len + strlen(pathname);
82-
ce = xcalloc(1, cache_entry_size(len));
82+
ce = make_empty_cache_entry(&the_index, len);
8383
oidcpy(&ce->oid, oid);
8484
memcpy(ce->name, base->buf, base->len);
8585
memcpy(ce->name + base->len, pathname, len - base->len);
@@ -98,7 +98,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
9898
if (ce->ce_mode == old->ce_mode &&
9999
!oidcmp(&ce->oid, &old->oid)) {
100100
old->ce_flags |= CE_UPDATE;
101-
free(ce);
101+
discard_cache_entry(ce);
102102
return 0;
103103
}
104104
}
@@ -232,11 +232,11 @@ static int checkout_merged(int pos, const struct checkout *state)
232232
if (write_object_file(result_buf.ptr, result_buf.size, blob_type, &oid))
233233
die(_("Unable to add merge result for '%s'"), path);
234234
free(result_buf.ptr);
235-
ce = make_cache_entry(mode, oid.hash, path, 2, 0);
235+
ce = make_transient_cache_entry(mode, &oid, path, 2);
236236
if (!ce)
237237
die(_("make_cache_entry failed for path '%s'"), path);
238238
status = checkout_entry(ce, state, NULL);
239-
free(ce);
239+
discard_cache_entry(ce);
240240
return status;
241241
}
242242

builtin/difftool.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -322,10 +322,10 @@ static int checkout_path(unsigned mode, struct object_id *oid,
322322
struct cache_entry *ce;
323323
int ret;
324324

325-
ce = make_cache_entry(mode, oid->hash, path, 0, 0);
325+
ce = make_transient_cache_entry(mode, oid, path, 0);
326326
ret = checkout_entry(ce, state, NULL);
327327

328-
free(ce);
328+
discard_cache_entry(ce);
329329
return ret;
330330
}
331331

@@ -489,7 +489,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
489489
* index.
490490
*/
491491
struct cache_entry *ce2 =
492-
make_cache_entry(rmode, roid.hash,
492+
make_cache_entry(&wtindex, rmode, &roid,
493493
dst_path, 0, 0);
494494

495495
add_index_entry(&wtindex, ce2,

builtin/reset.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ static void update_index_from_diff(struct diff_queue_struct *q,
134134
continue;
135135
}
136136

137-
ce = make_cache_entry(one->mode, one->oid.hash, one->path,
137+
ce = make_cache_entry(&the_index, one->mode, &one->oid, one->path,
138138
0, 0);
139139
if (!ce)
140140
die(_("make_cache_entry failed for path '%s'"),

builtin/update-index.c

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -268,15 +268,14 @@ static int process_lstat_error(const char *path, int err)
268268

269269
static int add_one_path(const struct cache_entry *old, const char *path, int len, struct stat *st)
270270
{
271-
int option, size;
271+
int option;
272272
struct cache_entry *ce;
273273

274274
/* Was the old index entry already up-to-date? */
275275
if (old && !ce_stage(old) && !ce_match_stat(old, st, 0))
276276
return 0;
277277

278-
size = cache_entry_size(len);
279-
ce = xcalloc(1, size);
278+
ce = make_empty_cache_entry(&the_index, len);
280279
memcpy(ce->name, path, len);
281280
ce->ce_flags = create_ce_flags(0);
282281
ce->ce_namelen = len;
@@ -285,13 +284,13 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
285284

286285
if (index_path(&ce->oid, path, st,
287286
info_only ? 0 : HASH_WRITE_OBJECT)) {
288-
free(ce);
287+
discard_cache_entry(ce);
289288
return -1;
290289
}
291290
option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
292291
option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
293292
if (add_cache_entry(ce, option)) {
294-
free(ce);
293+
discard_cache_entry(ce);
295294
return error("%s: cannot add to the index - missing --add option?", path);
296295
}
297296
return 0;
@@ -402,15 +401,14 @@ static int process_path(const char *path, struct stat *st, int stat_errno)
402401
static int add_cacheinfo(unsigned int mode, const struct object_id *oid,
403402
const char *path, int stage)
404403
{
405-
int size, len, option;
404+
int len, option;
406405
struct cache_entry *ce;
407406

408407
if (!verify_path(path, mode))
409408
return error("Invalid path '%s'", path);
410409

411410
len = strlen(path);
412-
size = cache_entry_size(len);
413-
ce = xcalloc(1, size);
411+
ce = make_empty_cache_entry(&the_index, len);
414412

415413
oidcpy(&ce->oid, oid);
416414
memcpy(ce->name, path, len);
@@ -600,7 +598,6 @@ static struct cache_entry *read_one_ent(const char *which,
600598
{
601599
unsigned mode;
602600
struct object_id oid;
603-
int size;
604601
struct cache_entry *ce;
605602

606603
if (get_tree_entry(ent, path, &oid, &mode)) {
@@ -613,8 +610,7 @@ static struct cache_entry *read_one_ent(const char *which,
613610
error("%s: not a blob in %s branch.", path, which);
614611
return NULL;
615612
}
616-
size = cache_entry_size(namelen);
617-
ce = xcalloc(1, size);
613+
ce = make_empty_cache_entry(&the_index, namelen);
618614

619615
oidcpy(&ce->oid, &oid);
620616
memcpy(ce->name, path, namelen);
@@ -691,8 +687,8 @@ static int unresolve_one(const char *path)
691687
error("%s: cannot add their version to the index.", path);
692688
ret = -1;
693689
free_return:
694-
free(ce_2);
695-
free(ce_3);
690+
discard_cache_entry(ce_2);
691+
discard_cache_entry(ce_3);
696692
return ret;
697693
}
698694

@@ -759,7 +755,7 @@ static int do_reupdate(int ac, const char **av,
759755
ce->name, ce_namelen(ce), 0);
760756
if (old && ce->ce_mode == old->ce_mode &&
761757
!oidcmp(&ce->oid, &old->oid)) {
762-
free(old);
758+
discard_cache_entry(old);
763759
continue; /* unchanged */
764760
}
765761
/* Be careful. The working tree may not have the
@@ -770,7 +766,7 @@ static int do_reupdate(int ac, const char **av,
770766
path = xstrdup(ce->name);
771767
update_one(path);
772768
free(path);
773-
free(old);
769+
discard_cache_entry(old);
774770
if (save_nr != active_nr)
775771
goto redo;
776772
}

cache.h

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "path.h"
1616
#include "sha1-array.h"
1717
#include "repository.h"
18+
#include "mem-pool.h"
1819

1920
#include <zlib.h>
2021
typedef struct git_zstream {
@@ -156,6 +157,7 @@ struct cache_entry {
156157
struct stat_data ce_stat_data;
157158
unsigned int ce_mode;
158159
unsigned int ce_flags;
160+
unsigned int mem_pool_allocated;
159161
unsigned int ce_namelen;
160162
unsigned int index; /* for link extension */
161163
struct object_id oid;
@@ -227,6 +229,7 @@ static inline void copy_cache_entry(struct cache_entry *dst,
227229
const struct cache_entry *src)
228230
{
229231
unsigned int state = dst->ce_flags & CE_HASHED;
232+
int mem_pool_allocated = dst->mem_pool_allocated;
230233

231234
/* Don't copy hash chain and name */
232235
memcpy(&dst->ce_stat_data, &src->ce_stat_data,
@@ -235,6 +238,9 @@ static inline void copy_cache_entry(struct cache_entry *dst,
235238

236239
/* Restore the hash state */
237240
dst->ce_flags = (dst->ce_flags & ~CE_HASHED) | state;
241+
242+
/* Restore the mem_pool_allocated flag */
243+
dst->mem_pool_allocated = mem_pool_allocated;
238244
}
239245

240246
static inline unsigned create_ce_flags(unsigned stage)
@@ -328,6 +334,7 @@ struct index_state {
328334
struct untracked_cache *untracked;
329335
uint64_t fsmonitor_last_update;
330336
struct ewah_bitmap *fsmonitor_dirty;
337+
struct mem_pool *ce_mem_pool;
331338
};
332339

333340
extern struct index_state the_index;
@@ -339,6 +346,60 @@ extern void remove_name_hash(struct index_state *istate, struct cache_entry *ce)
339346
extern void free_name_hash(struct index_state *istate);
340347

341348

349+
/* Cache entry creation and cleanup */
350+
351+
/*
352+
* Create cache_entry intended for use in the specified index. Caller
353+
* is responsible for discarding the cache_entry with
354+
* `discard_cache_entry`.
355+
*/
356+
struct cache_entry *make_cache_entry(struct index_state *istate,
357+
unsigned int mode,
358+
const struct object_id *oid,
359+
const char *path,
360+
int stage,
361+
unsigned int refresh_options);
362+
363+
struct cache_entry *make_empty_cache_entry(struct index_state *istate,
364+
size_t name_len);
365+
366+
/*
367+
* Create a cache_entry that is not intended to be added to an index.
368+
* Caller is responsible for discarding the cache_entry
369+
* with `discard_cache_entry`.
370+
*/
371+
struct cache_entry *make_transient_cache_entry(unsigned int mode,
372+
const struct object_id *oid,
373+
const char *path,
374+
int stage);
375+
376+
struct cache_entry *make_empty_transient_cache_entry(size_t name_len);
377+
378+
/*
379+
* Discard cache entry.
380+
*/
381+
void discard_cache_entry(struct cache_entry *ce);
382+
383+
/*
384+
* Check configuration if we should perform extra validation on cache
385+
* entries.
386+
*/
387+
int should_validate_cache_entries(void);
388+
389+
/*
390+
* Duplicate a cache_entry. Allocate memory for the new entry from a
391+
* memory_pool. Takes into account cache_entry fields that are meant
392+
* for managing the underlying memory allocation of the cache_entry.
393+
*/
394+
struct cache_entry *dup_cache_entry(const struct cache_entry *ce, struct index_state *istate);
395+
396+
/*
397+
* Validate the cache entries in the index. This is an internal
398+
* consistency check that the cache_entry structs are allocated from
399+
* the expected memory pool.
400+
*/
401+
void validate_cache_entries(const struct index_state *istate);
402+
342403
#ifndef NO_THE_INDEX_COMPATIBILITY_MACROS
343404
#define active_cache (the_index.cache)
344405
#define active_nr (the_index.cache_nr)
@@ -698,7 +759,6 @@ extern int remove_file_from_index(struct index_state *, const char *path);
698759
extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
699760
extern int add_file_to_index(struct index_state *, const char *path, int flags);
700761

701-
extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, unsigned int refresh_options);
702762
extern int chmod_index_entry(struct index_state *, struct cache_entry *ce, char flip);
703763
extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
704764
extern void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
@@ -751,7 +811,7 @@ extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
751811
#define REFRESH_IGNORE_SUBMODULES 0x0010 /* ignore submodules */
752812
#define REFRESH_IN_PORCELAIN 0x0020 /* user friendly output, not "needs update" */
753813
extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
754-
extern struct cache_entry *refresh_cache_entry(struct cache_entry *, unsigned int);
814+
extern struct cache_entry *refresh_cache_entry(struct index_state *, struct cache_entry *, unsigned int);
755815

756816
/*
757817
* Opportunistically update the index but do not complain if we can't.

0 commit comments

Comments
 (0)