name-rev: sort tip names before applying

name_ref() is called for each ref and checks if its a better name for
the referenced commit.  If that's the case it remembers it and checks if
a name based on it is better for its ancestors as well.  This in done in
the the order for_each_ref() imposes on us.

That might not be optimal.  If bad names happen to be encountered first
(as defined by is_better_name()), names derived from them may spread to
a lot of commits, only to be replaced by better names later.  Setting
better names first can avoid that.

is_better_name() prefers tags, short distances and old references.  The
distance is a measure that we need to calculate for each candidate
commit, but the other two properties are not dependent on the
relationships of commits.  Sorting the refs by them should yield better
performance than the essentially random order we currently use.

And applying older references first should also help to reduce rework
due to the fact that older commits have less ancestors than newer ones.

So add all details of names to the tip table first, then sort them
to prefer tags and older references and then apply them in this order.
Here's the performance as measures by hyperfine for the Linux repo
before:

Benchmark #1: ./git -C ../linux/ name-rev --all
  Time (mean ± σ):     851.1 ms ±   4.5 ms    [User: 806.7 ms, System: 44.4 ms]
  Range (min … max):   845.9 ms … 859.5 ms    10 runs

... and with this patch:

Benchmark #1: ./git -C ../linux/ name-rev --all
  Time (mean ± σ):     736.2 ms ±   8.7 ms    [User: 688.4 ms, System: 47.5 ms]
  Range (min … max):   726.0 ms … 755.2 ms    10 runs

Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
René Scharfe 2020-02-05 18:50:23 +01:00 коммит произвёл Junio C Hamano
Родитель 2d53975488
Коммит 079f970971
1 изменённых файлов: 52 добавлений и 8 удалений

Просмотреть файл

@ -247,6 +247,10 @@ static struct tip_table {
struct tip_table_entry { struct tip_table_entry {
struct object_id oid; struct object_id oid;
const char *refname; const char *refname;
struct commit *commit;
timestamp_t taggerdate;
unsigned int from_tag:1;
unsigned int deref:1;
} *table; } *table;
int nr; int nr;
int alloc; int alloc;
@ -254,13 +258,18 @@ static struct tip_table {
} tip_table; } tip_table;
static void add_to_tip_table(const struct object_id *oid, const char *refname, static void add_to_tip_table(const struct object_id *oid, const char *refname,
int shorten_unambiguous) int shorten_unambiguous, struct commit *commit,
timestamp_t taggerdate, int from_tag, int deref)
{ {
refname = name_ref_abbrev(refname, shorten_unambiguous); refname = name_ref_abbrev(refname, shorten_unambiguous);
ALLOC_GROW(tip_table.table, tip_table.nr + 1, tip_table.alloc); ALLOC_GROW(tip_table.table, tip_table.nr + 1, tip_table.alloc);
oidcpy(&tip_table.table[tip_table.nr].oid, oid); oidcpy(&tip_table.table[tip_table.nr].oid, oid);
tip_table.table[tip_table.nr].refname = xstrdup(refname); tip_table.table[tip_table.nr].refname = xstrdup(refname);
tip_table.table[tip_table.nr].commit = commit;
tip_table.table[tip_table.nr].taggerdate = taggerdate;
tip_table.table[tip_table.nr].from_tag = from_tag;
tip_table.table[tip_table.nr].deref = deref;
tip_table.nr++; tip_table.nr++;
tip_table.sorted = 0; tip_table.sorted = 0;
} }
@ -271,12 +280,30 @@ static int tipcmp(const void *a_, const void *b_)
return oidcmp(&a->oid, &b->oid); return oidcmp(&a->oid, &b->oid);
} }
static int cmp_by_tag_and_age(const void *a_, const void *b_)
{
const struct tip_table_entry *a = a_, *b = b_;
int cmp;
/* Prefer tags. */
cmp = b->from_tag - a->from_tag;
if (cmp)
return cmp;
/* Older is better. */
if (a->taggerdate < b->taggerdate)
return -1;
return a->taggerdate != b->taggerdate;
}
static int name_ref(const char *path, const struct object_id *oid, int flags, void *cb_data) static int name_ref(const char *path, const struct object_id *oid, int flags, void *cb_data)
{ {
struct object *o = parse_object(the_repository, oid); struct object *o = parse_object(the_repository, oid);
struct name_ref_data *data = cb_data; struct name_ref_data *data = cb_data;
int can_abbreviate_output = data->tags_only && data->name_only; int can_abbreviate_output = data->tags_only && data->name_only;
int deref = 0; int deref = 0;
int from_tag = 0;
struct commit *commit = NULL;
timestamp_t taggerdate = TIME_MAX; timestamp_t taggerdate = TIME_MAX;
if (data->tags_only && !starts_with(path, "refs/tags/")) if (data->tags_only && !starts_with(path, "refs/tags/"))
@ -325,8 +352,6 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
return 0; return 0;
} }
add_to_tip_table(oid, path, can_abbreviate_output);
while (o && o->type == OBJ_TAG) { while (o && o->type == OBJ_TAG) {
struct tag *t = (struct tag *) o; struct tag *t = (struct tag *) o;
if (!t->tagged) if (!t->tagged)
@ -336,17 +361,35 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
taggerdate = t->date; taggerdate = t->date;
} }
if (o && o->type == OBJ_COMMIT) { if (o && o->type == OBJ_COMMIT) {
struct commit *commit = (struct commit *)o; commit = (struct commit *)o;
int from_tag = starts_with(path, "refs/tags/"); from_tag = starts_with(path, "refs/tags/");
if (taggerdate == TIME_MAX) if (taggerdate == TIME_MAX)
taggerdate = commit->date; taggerdate = commit->date;
path = name_ref_abbrev(path, can_abbreviate_output);
name_rev(commit, path, taggerdate, from_tag, deref);
} }
add_to_tip_table(oid, path, can_abbreviate_output, commit, taggerdate,
from_tag, deref);
return 0; return 0;
} }
static void name_tips(void)
{
int i;
/*
* Try to set better names first, so that worse ones spread
* less.
*/
QSORT(tip_table.table, tip_table.nr, cmp_by_tag_and_age);
for (i = 0; i < tip_table.nr; i++) {
struct tip_table_entry *e = &tip_table.table[i];
if (e->commit) {
name_rev(e->commit, e->refname, e->taggerdate,
e->from_tag, e->deref);
}
}
}
static const unsigned char *nth_tip_table_ent(size_t ix, void *table_) static const unsigned char *nth_tip_table_ent(size_t ix, void *table_)
{ {
struct tip_table_entry *table = table_; struct tip_table_entry *table = table_;
@ -559,6 +602,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
cutoff = TIME_MIN; cutoff = TIME_MIN;
} }
for_each_ref(name_ref, &data); for_each_ref(name_ref, &data);
name_tips();
if (transform_stdin) { if (transform_stdin) {
char buffer[2048]; char buffer[2048];