"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Stefan Sperling <stsp@stsp.name>
Subject:
speed up 'got histedit -l' and 'got rebase -l'
To:
gameoftrees@openbsd.org
Date:
Sun, 10 Oct 2021 18:03:43 +0200

Download raw body.

Thread
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);