From 10b7fc2b25f21deb66f472a731651620c0caaac9 Mon Sep 17 00:00:00 2001 From: akshay-venkatesh Date: Wed, 17 Feb 2021 11:13:42 -0800 Subject: [PATCH] UCS/TOPO: Use common prefix and char count for path distance estimation --- src/ucs/sys/string.c | 56 ++++++++++++++++++++++++----------- src/ucs/sys/string.h | 32 +++++++++++++++++--- src/ucs/sys/topo.c | 14 +++------ test/gtest/ucs/test_string.cc | 33 +++++++++++++++++++++ 4 files changed, 104 insertions(+), 31 deletions(-) diff --git a/src/ucs/sys/string.c b/src/ucs/sys/string.c index 98a5a705b44..f7572dbfb14 100644 --- a/src/ucs/sys/string.c +++ b/src/ucs/sys/string.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -306,33 +307,54 @@ const char* ucs_flags_str(char *buf, size_t max, return buf; } +size_t ucs_string_count_char(const char *str, char c) +{ + size_t count = 0; + const char *p; + + for (p = str; *p != '\0'; ++p) { + if (*p == c) { + count++; + } + } + + return count; +} + +size_t ucs_string_common_prefix_len(const char *str1, const char *str2) +{ + const char *p1 = str1; + const char *p2 = str2; + + /* as long as *p1==*p2, if *p1 is not '\0' then neither is *p2 */ + while ((*p1 != '\0') && (*p1 == *p2)) { + p1++; + p2++; + } + + return (p1 - str1); +} + ssize_t ucs_path_calc_distance(const char *path1, const char *path2) { unsigned distance = 0; - int same = 1; - char resolved_path1[PATH_MAX], resolved_path2[PATH_MAX]; - size_t comp_len, i; - size_t rp_len1, rp_len2; + size_t common_length; + char rpath1[PATH_MAX], rpath2[PATH_MAX]; - if ((NULL == realpath(path1, resolved_path1)) || - (NULL == realpath(path2, resolved_path2))) { + if ((NULL == realpath(path1, rpath1)) || + (NULL == realpath(path2, rpath2))) { return UCS_ERR_INVALID_PARAM; } - rp_len1 = strlen(resolved_path1); - rp_len2 = strlen(resolved_path2); - comp_len = ucs_min(rp_len1, rp_len2); + common_length = ucs_string_common_prefix_len(rpath1, rpath2); - for (i = 0; i < comp_len; i++) { - if (resolved_path1[i] != resolved_path2[i]) { - same = 0; - } - - if ((resolved_path1[i] == '/') && !same) { - distance++; - } + if (rpath1[common_length] != rpath2[common_length]) { + ++distance; /* count the differentiating path component */ } + distance += ucs_max(ucs_string_count_char(rpath1 + common_length, '/'), + ucs_string_count_char(rpath2 + common_length, '/')); + return distance; } diff --git a/src/ucs/sys/string.h b/src/ucs/sys/string.h index c398aee7a86..7b1bfe9d267 100644 --- a/src/ucs/sys/string.h +++ b/src/ucs/sys/string.h @@ -218,14 +218,38 @@ const char* ucs_flags_str(char *str, size_t max, /** - * Get estimated number of segments different in the two paths. Segments are - * separated by `/`. + * Find the number of occurences of a char in the given string. + * + * @param str String buffer to search. + * @param c Character to search in the string. + * + * @return a value between 0 and strlen(str). + */ +size_t ucs_string_count_char(const char *str, char c); + + +/** + * Length of the common string from the start of two given strings. + * + * @param str1 First string buffer. + * @param str2 Second string buffer. + * + * @return a value between 0 and min(strlen(str1), strlen(str2)). + */ +size_t ucs_string_common_prefix_len(const char *str1, const char *str2); + + +/** + * Get number of segments that are disimilar in the two paths. Segments + * are separated by `/`. When the number of segments are unequal for the given + * paths, the number of segments different in the larger of the paths is + * returned. E.g. for /a/b/c/d and /a/x/y 3 is returned; for /a/b/c/d and + * /a/b/c/e 1 is returned; for /a/b/c and /a/b/c 0 is returned * * @param path1 String pointing to first path * @param path2 String pointing to second path * - * @return if either of the paths are invalid, UINT_MAX; if paths are the same 0 - * is returned; otherwise in between + * @return if either of the paths are invalid UINT_MAX is returned. */ ssize_t ucs_path_calc_distance(const char *path1, const char *path2); diff --git a/src/ucs/sys/topo.c b/src/ucs/sys/topo.c index cf9852b6e92..f42916fbadc 100644 --- a/src/ucs/sys/topo.c +++ b/src/ucs/sys/topo.c @@ -137,16 +137,10 @@ ucs_status_t ucs_topo_get_distance(ucs_sys_device_t device1, * TODO implement more accurate estimation, based on system type, PCIe * switch, etc. */ - if (path_distance <= 2) { - distance->latency = 0; - distance->bandwidth = DBL_MAX; - } else if (path_distance <= 4) { - distance->latency = 300e-9; - distance->bandwidth = 2000 * UCS_MBYTE; - } else { - distance->latency = 900e-9; - distance->bandwidth = 300 * UCS_MBYTE; - } + distance->latency = 100e-9 * path_distance; + distance->bandwidth = (path_distance == 0) ? + DBL_MAX : + ((20000 / path_distance) * UCS_MBYTE); return UCS_OK; } diff --git a/test/gtest/ucs/test_string.cc b/test/gtest/ucs/test_string.cc index 8edf5dd794b..10abe5641a6 100644 --- a/test/gtest/ucs/test_string.cc +++ b/test/gtest/ucs/test_string.cc @@ -23,6 +23,39 @@ class test_string : public ucs::test { } }; +UCS_TEST_F(test_string, count_char) { + static const char *str1 = "/foo"; + static const char *str2 = "/foo/bar"; + size_t count; + + count = ucs_string_count_char(str1, '/'); + EXPECT_EQ(1, count); + + count = ucs_string_count_char((const char*)UCS_PTR_BYTE_OFFSET(str1, 1), + '/'); + EXPECT_EQ(0, count); + + count = ucs_string_count_char(str2, '/'); + EXPECT_EQ(2, count); + + count = ucs_string_count_char((const char*)UCS_PTR_BYTE_OFFSET(str2, 1), + '/'); + EXPECT_EQ(1, count); +} + +UCS_TEST_F(test_string, common_prefix_len) { + static const char *str1 = "/foo"; + static const char *str2 = "/foobar"; + static const char *str3 = "foo/bar"; + size_t common_length; + + common_length = ucs_string_common_prefix_len(str1, str2); + EXPECT_EQ(4, common_length); + + common_length = ucs_string_common_prefix_len(str1, str3); + EXPECT_EQ(0, common_length); +} + UCS_TEST_F(test_string, trim) { char str1[] = " foo "; EXPECT_EQ("foo", std::string(ucs_strtrim(str1)));