commit-slab: avoid large realloc

Instead of using a single "slab" and keep reallocating it as we find that we need to deal with commits with larger values of commit->index, make a "slab" an array of many "slab_piece"s. Each access may need two levels of indirections, but we only need to reallocate the first level array of pointers when we have to grow the table this way. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Apr 12, 2013 at 23:25 UTC 66eb375d3d334efa3f467775a5f2a647c131c4b1
1 file changed +42 -20
commit.c
+42 -20
@@ -501,34 +501,56 @@ struct commit *pop_commit(struct commit_list **stack)
501 return item;
502 }
503
504 +struct commit_slab_piece {
505 + int buf;
506 +};
507 +
508 struct commit_slab {
505 - int *buf;
506 - int alloc;
509 + int piece_size;
510 + int piece_count;
511 + struct commit_slab_piece **piece;
512 };
513
514 static void slab_init(struct commit_slab *s)
515 {
511 - memset(s, 0, sizeof(*s));
516 + /* allocate ~512kB at once, allowing for malloc overhead */
517 + int size = (512*1024-32) / sizeof(struct commit_slab_piece);
518 +
519 + s->piece_size = size;
520 + s->piece_count = 0;
521 + s->piece = NULL;
522 }
523
524 static void slab_clear(struct commit_slab *s)
525 {
516 - free(s->buf);
517 - slab_init(s);
526 + int i;
527 +
528 + for (i = 0; i < s->piece_count; i++)
529 + free(s->piece[i]);
530 + s->piece_count = 0;
531 + free(s->piece);
532 + s->piece = NULL;
533 }
534
520 -static inline int *slab_at(struct commit_slab *s, const struct commit *c)
535 +static inline struct commit_slab_piece *slab_at(struct commit_slab *s,
536 + const struct commit *c)
537 {
522 - if (s->alloc <= c->index) {
523 - int new_alloc = alloc_nr(s->alloc);
524 - if (new_alloc <= c->index)
525 - new_alloc = c->index + 1;
526 -
527 - s->buf = xrealloc(s->buf, new_alloc * sizeof(*s->buf));
528 - memset(s->buf + s->alloc, 0, new_alloc - s->alloc);
529 - s->alloc = new_alloc;
538 + int nth_piece, nth_slot;
539 +
540 + nth_piece = c->index / s->piece_size;
541 + nth_slot = c->index % s->piece_size;
542 +
543 + if (s->piece_count <= nth_piece) {
544 + int i;
545 +
546 + s->piece = xrealloc(s->piece, (nth_piece + 1) * sizeof(s->piece));
547 + for (i = s->piece_count; i <= nth_piece; i++)
548 + s->piece[i] = NULL;
549 + s->piece_count = nth_piece + 1;
550 }
531 - return s->buf + c->index;
551 + if (!s->piece[nth_piece])
552 + s->piece[nth_piece] = xcalloc(s->piece_size, sizeof(**s->piece));
553 + return &s->piece[nth_piece][nth_slot];
554 }
555
556 /*
@@ -550,7 +572,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo)
572 /* Mark them and clear the indegree */
573 for (next = orig; next; next = next->next) {
574 struct commit *commit = next->item;
553 - *slab_at(&indegree, commit) = 1;
575 + slab_at(&indegree, commit)->buf = 1;
576 }
577
578 /* update the indegree */
@@ -558,7 +580,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo)
580 struct commit_list * parents = next->item->parents;
581 while (parents) {
582 struct commit *parent = parents->item;
561 - int *pi = slab_at(&indegree, parent);
583 + int *pi = &slab_at(&indegree, parent)->buf;
584
585 if (*pi)
586 (*pi)++;
@@ -578,7 +600,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo)
600 for (next = orig; next; next = next->next) {
601 struct commit *commit = next->item;
602
581 - if (*slab_at(&indegree, commit) == 1)
603 + if (slab_at(&indegree, commit)->buf == 1)
604 insert = &commit_list_insert(commit, insert)->next;
605 }
606
@@ -599,7 +621,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo)
621 commit = work_item->item;
622 for (parents = commit->parents; parents ; parents = parents->next) {
623 struct commit *parent = parents->item;
602 - int *pi = slab_at(&indegree, parent);
624 + int *pi = &slab_at(&indegree, parent)->buf;
625
626 if (!*pi)
627 continue;
@@ -620,7 +642,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo)
642 * work_item is a commit all of whose children
643 * have already been emitted. we can emit it now.
644 */
623 - *slab_at(&indegree, commit) = 0;
645 + slab_at(&indegree, commit)->buf = 0;
646 *pptr = work_item;
647 pptr = &work_item->next;
648 }