Commit 2dde1e0c1c398708d6a84763015e690f23cc7511

Carlos Martín Nieto 2014-05-08T22:31:59

indexer: avoid memory moves Our vector does a move of the rest of the array when we remove an item. Doing this repeatedly can be expensive, and we do this a lot in the indexer. Instead, set the value to NULL and skip those entries. perf reported around 30% of `index-pack` time was going into memmove. With this change, that goes away and we spent most of the time hashing and inflating data.

diff --git a/src/indexer.c b/src/indexer.c
index adf5cea..3c8415c 100644
--- a/src/indexer.c
+++ b/src/indexer.c
@@ -717,6 +717,9 @@ static int fix_thin_pack(git_indexer *idx, git_transfer_progress *stats)
 
 	/* Loop until we find the first REF delta */
 	git_vector_foreach(&idx->deltas, i, delta) {
+		if (!delta)
+			continue;
+
 		curpos = delta->delta_off;
 		error = git_packfile_unpack_header(&size, &type, &idx->pack->mwf, &w, &curpos);
 		git_mwindow_close(&w);
@@ -756,13 +759,18 @@ static int resolve_deltas(git_indexer *idx, git_transfer_progress *stats)
 {
 	unsigned int i;
 	struct delta_info *delta;
-	int progressed = 0, progress_cb_result;
+	int progressed = 0, non_null = 0, progress_cb_result;
 
 	while (idx->deltas.length > 0) {
 		progressed = 0;
+		non_null = 0;
 		git_vector_foreach(&idx->deltas, i, delta) {
 			git_rawobj obj;
 
+			if (!delta)
+				continue;
+
+			non_null = 1;
 			idx->off = delta->delta_off;
 			if (git_packfile_unpack(&obj, idx->pack, &idx->off) < 0)
 				continue;
@@ -777,16 +785,15 @@ static int resolve_deltas(git_indexer *idx, git_transfer_progress *stats)
 			if ((progress_cb_result = do_progress_callback(idx, stats)) < 0)
 				return progress_cb_result;
 
-			/*
-			 * Remove this delta from the list and
-			 * decrease i so we don't skip over the next
-			 * delta.
-			 */
-			git_vector_remove(&idx->deltas, i);
+			/* remove from the list */
+			git_vector_set(NULL, &idx->deltas, i, NULL);
 			git__free(delta);
-			i--;
 		}
 
+		/* if none were actually set, we're done */
+		if (!non_null)
+			break;
+
 		if (!progressed && (fix_thin_pack(idx, stats) < 0)) {
 			giterr_set(GITERR_INDEXER, "missing delta bases");
 			return -1;