read-cache: speed up has_dir_name (part 1)
Teach has_dir_name() to see if the path of the new item is greater than the last path in the index array before attempting to search for it. has_dir_name() is looking for file/directory collisions in the index and has to consider each sub-directory prefix in turn. This can cause multiple binary searches for each path. During operations like checkout, merge_working_tree() populates the new index in sorted order, so we expect to be able to append in many cases. This commit is part 1 of 2. This commit handles the top of has_dir_name() and the trivial optimization. Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Jeff Hostetler committed
Apr 19, 2017 at 17:06 UTC
06b6d81b7929ce6f5542ba869e7bb9d8e437e6f8
1 file changed
+45
read-cache.c
+45
@@ -910,6 +910,9 @@ int strcmp_offset(const char *s1, const char *s2, size_t *first_change)
910
/*
911
* Do we have another file with a pathname that is a proper
912
* subset of the name we're trying to add?
913
+ *
914
+ * That is, is there another file in the index with a path
915
+ * that matches a sub-directory in the given entry?
916
*/
917
static int has_dir_name(struct index_state *istate,
918
const struct cache_entry *ce, int pos, int ok_to_replace)
@@ -918,6 +921,48 @@ static int has_dir_name(struct index_state *istate,
921
int stage = ce_stage(ce);
922
const char *name = ce->name;
923
const char *slash = name + ce_namelen(ce);
924
+ size_t len_eq_last;
925
+ int cmp_last = 0;
926
+
927
+ /*
928
+ * We are frequently called during an iteration on a sorted
929
+ * list of pathnames and while building a new index. Therefore,
930
+ * there is a high probability that this entry will eventually
931
+ * be appended to the index, rather than inserted in the middle.
932
+ * If we can confirm that, we can avoid binary searches on the
933
+ * components of the pathname.
934
+ *
935
+ * Compare the entry's full path with the last path in the index.
936
+ */
937
+ if (istate->cache_nr > 0) {
938
+ cmp_last = strcmp_offset(name,
939
+ istate->cache[istate->cache_nr - 1]->name,
940
+ &len_eq_last);
941
+ if (cmp_last > 0) {
942
+ if (len_eq_last == 0) {
943
+ /*
944
+ * The entry sorts AFTER the last one in the
945
+ * index and their paths have no common prefix,
946
+ * so there cannot be a F/D conflict.
947
+ */
948
+ return retval;
949
+ } else {
950
+ /*
951
+ * The entry sorts AFTER the last one in the
952
+ * index, but has a common prefix. Fall through
953
+ * to the loop below to disect the entry's path
954
+ * and see where the difference is.
955
+ */
956
+ }
957
+ } else if (cmp_last == 0) {
958
+ /*
959
+ * The entry exactly matches the last one in the
960
+ * index, but because of multiple stage and CE_REMOVE
961
+ * items, we fall through and let the regular search
962
+ * code handle it.
963
+ */
964
+ }
965
+ }
966
967
for (;;) {
968
int len;