From: Stefan Sperling Subject: speed up 'got histedit -l' and 'got rebase -l' To: gameoftrees@openbsd.org Date: Sun, 10 Oct 2021 18:03:43 +0200 I have found that listing histedit backups took a fairly long time on one of my machines. Before producing any output the command 'got histedit -l' was crunching away on the CPU for several seconds. Profiling revealed the bottleneck: We are opening and closing commit objects over and over while sorting references by commit timestamp. I have about 300 backup references in my got.git repository, and sorting these by commit timestamp results in commits being opened and closed more than 28.000 times. See the attached profiling graph. Caching commit timestamps in struct got_reference allows sorting to complete a lot more quickly. 'got histedit -l' now starts producing output almost immediately. ok? diff 072b512fffc05c5484d6e18be44ae672c11613b2 a3774042d47a891f7f7994479c470b945aa686c6 blob - debec1726d7defbd2dd5322b460ae1d739329fb3 blob + 23650d4ec25dae858e6e634a02dd5735b0ac0b9d --- lib/reference.c +++ lib/reference.c @@ -88,6 +88,9 @@ struct got_reference { struct got_lockfile *lf; time_t mtime; + + /* Cached timestamp for got_ref_cmp_by_commit_timestamp_descending() */ + time_t committer_time; }; static const struct got_error * @@ -772,31 +775,36 @@ got_ref_cmp_by_commit_timestamp_descending(void *arg, { const struct got_error *err; struct got_repository *repo = arg; - struct got_object_id *id1, *id2 = NULL; + struct got_object_id *id1 = NULL, *id2 = NULL; struct got_commit_object *commit1 = NULL, *commit2 = NULL; - time_t time1, time2; *cmp = 0; - err = got_ref_resolve(&id1, repo, ref1); - if (err) - return err; - err = got_ref_resolve(&id2, repo, ref2); - if (err) - goto done; + if (ref1->committer_time == 0) { + err = got_ref_resolve(&id1, repo, ref1); + if (err) + return err; + err = got_object_open_as_commit(&commit1, repo, id1); + if (err) + goto done; + ref1->committer_time = + got_object_commit_get_committer_time(commit1); + } - err = got_object_open_as_commit(&commit1, repo, id1); - if (err) - goto done; - err = got_object_open_as_commit(&commit2, repo, id2); - if (err) - goto done; - - time1 = got_object_commit_get_committer_time(commit1); - time2 = got_object_commit_get_committer_time(commit2); - if (time1 < time2) + if (ref2->committer_time == 0) { + err = got_ref_resolve(&id2, repo, ref2); + if (err) + return err; + err = got_object_open_as_commit(&commit2, repo, id2); + if (err) + goto done; + ref2->committer_time = + got_object_commit_get_committer_time(commit2); + } + + if (ref1->committer_time < ref2->committer_time) *cmp = 1; - else if (time2 < time1) + else if (ref2->committer_time < ref1->committer_time) *cmp = -1; done: free(id1);