From: Christian Kamm Date: Fri, 21 Aug 2015 12:20:27 +0000 (+0200) Subject: csync_exclude: Speed up siginificantly #3638 X-Git-Tag: archive/raspbian/3.16.7-1_deb13u1+rpi1~1^2~1408^2~170 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=20f4ec6b6756ced80cfc92a5a891ec353b25055d;p=nextcloud-desktop.git csync_exclude: Speed up siginificantly #3638 The performance test indicates a speedup of around 3x for paths with around 12 components. --- diff --git a/csync/src/csync_exclude.c b/csync/src/csync_exclude.c index 7a29e37e3..42adfe8ef 100644 --- a/csync/src/csync_exclude.c +++ b/csync/src/csync_exclude.c @@ -191,7 +191,6 @@ CSYNC_EXCLUDE_TYPE csync_excluded_no_ctx(c_strlist_t *excludes, const char *path const char *p = NULL; char *bname = NULL; char *dname = NULL; - char *prev_dname = NULL; char *conflict = NULL; int rc = -1; CSYNC_EXCLUDE_TYPE match = CSYNC_NOT_EXCLUDED; @@ -340,54 +339,44 @@ CSYNC_EXCLUDE_TYPE csync_excluded_no_ctx(c_strlist_t *excludes, const char *path } } - /* if still not excluded, check each component of the path */ + /* if still not excluded, check each component and leading directory of the path */ if (match == CSYNC_NOT_EXCLUDED) { - int trailing_component = 1; - dname = c_dirname(path); - bname = c_basename(path); - - if (bname == NULL || dname == NULL) { - match = CSYNC_NOT_EXCLUDED; - SAFE_FREE(bname); - SAFE_FREE(dname); - SAFE_FREE(pattern_stored); - goto out; - } + char *segmented_path = strdup(path); + size_t len = strlen(segmented_path); + bool check_segname = !match_dirs_only || filetype != CSYNC_FTW_TYPE_FILE; + for (int j = len; ; --j) { + // read backwards until a path separator + if (j != 0 && segmented_path[j-1] != '/') { + continue; + } - /* Check each component of the path */ - do { - /* Do not check the bname if its a file and the pattern matches dirs only. */ - if ( !(trailing_component == 1 /* it is the trailing component */ - && match_dirs_only /* but only directories are matched by the pattern */ - && filetype == CSYNC_FTW_TYPE_FILE) ) { - /* Check the name component against the pattern */ - rc = csync_fnmatch(pattern, bname, 0); + // check 'basename', i.e. for "/foo/bar/fi" we'd check 'fi', 'bar', 'foo' + if (check_segname && segmented_path[j] != 0) { + rc = csync_fnmatch(pattern, segmented_path + j, 0); if (rc == 0) { match = type; + break; } } - if (!(c_streq(dname, ".") || c_streq(dname, "/"))) { - rc = csync_fnmatch(pattern, dname, 0); - if (rc == 0) { - match = type; - } + check_segname = true; + + if (j == 0) { + break; + } + + // check 'dirname', i.e. for "/foo/bar/fi" we'd check '/foo/bar', '/foo' + segmented_path[j-1] = '\0'; + rc = csync_fnmatch(pattern, segmented_path, 0); + if (rc == 0) { + match = type; + break; } - trailing_component = 0; - prev_dname = dname; - SAFE_FREE(bname); - bname = c_basename(prev_dname); - dname = c_dirname(prev_dname); - SAFE_FREE(prev_dname); - - } while( match == CSYNC_NOT_EXCLUDED && !c_streq(dname, ".") - && !c_streq(dname, "/") ); + } + SAFE_FREE(segmented_path); } SAFE_FREE(pattern_stored); - SAFE_FREE(bname); - SAFE_FREE(dname); } - out: return match; diff --git a/csync/tests/csync_tests/check_csync_exclude.c b/csync/tests/csync_tests/check_csync_exclude.c index 94430eafc..b0b57ff99 100644 --- a/csync/tests/csync_tests/check_csync_exclude.c +++ b/csync/tests/csync_tests/check_csync_exclude.c @@ -19,6 +19,7 @@ */ #include "config_csync.h" #include +#include #include "torture.h" @@ -170,8 +171,25 @@ static void check_csync_pathes(void **state) rc = csync_excluded(csync, "meep/excl", CSYNC_FTW_TYPE_DIR); assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + rc = csync_excluded(csync, "meep/excl/file", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + rc = csync_excluded(csync, "/excl", CSYNC_FTW_TYPE_FILE); assert_int_equal(rc, CSYNC_NOT_EXCLUDED); + + _csync_exclude_add(&csync->excludes, "/excludepath/withsubdir"); + + rc = csync_excluded(csync, "/excludepath/withsubdir", CSYNC_FTW_TYPE_DIR); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + + rc = csync_excluded(csync, "/excludepath/withsubdir", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + + rc = csync_excluded(csync, "/excludepath/withsubdir2", CSYNC_FTW_TYPE_DIR); + assert_int_equal(rc, CSYNC_NOT_EXCLUDED); + + rc = csync_excluded(csync, "/excludepath/withsubdir/foo", CSYNC_FTW_TYPE_DIR); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); } static void check_csync_is_windows_reserved_word() { @@ -190,7 +208,31 @@ static void check_csync_is_windows_reserved_word() { assert_true(csync_is_windows_reserved_word("Z:")); assert_true(csync_is_windows_reserved_word("M:")); assert_true(csync_is_windows_reserved_word("m:")); +} + +static void check_csync_excluded_performance(void **state) +{ + CSYNC *csync = *state; + + const int N = 10000; + int totalRc = 0; + + // Being able to use QElapsedTimer for measurement would be nice... + struct timeval before, after; + gettimeofday(&before, 0); + + for (int i = 0; i < N; ++i) { + totalRc += csync_excluded(csync, "/this/is/quite/a/long/path/with/many/components", CSYNC_FTW_TYPE_DIR); + totalRc += csync_excluded(csync, "/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/29", CSYNC_FTW_TYPE_FILE); + } + assert_int_equal(totalRc, CSYNC_NOT_EXCLUDED); // mainly to avoid optimization + + gettimeofday(&after, 0); + const double total = (after.tv_sec - before.tv_sec) + + (after.tv_usec - before.tv_usec) / 1.0e6; + const double perCallMs = total / 2 / N * 1000; + printf("csync_excluded: %f ms per call\n", perCallMs); } int torture_run_tests(void) @@ -201,6 +243,7 @@ int torture_run_tests(void) unit_test_setup_teardown(check_csync_excluded, setup_init, teardown), unit_test_setup_teardown(check_csync_pathes, setup_init, teardown), unit_test_setup_teardown(check_csync_is_windows_reserved_word, setup_init, teardown), + unit_test_setup_teardown(check_csync_excluded_performance, setup_init, teardown), }; return run_tests(tests);