Skip to content

Commit

Permalink
UCS/TOPO: Use common prefix and char count for path distance estimation
Browse files Browse the repository at this point in the history
  • Loading branch information
Akshay-Venkatesh committed Feb 17, 2021
1 parent bbcc0d9 commit 10b7fc2
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 31 deletions.
56 changes: 39 additions & 17 deletions src/ucs/sys/string.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <ucs/config/parser.h>
#include <ucs/arch/bitops.h>
#include <ucs/sys/math.h>
#include <ucs/debug/log.h>

#include <string.h>
#include <ctype.h>
Expand Down Expand Up @@ -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;
}

Expand Down
32 changes: 28 additions & 4 deletions src/ucs/sys/string.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
14 changes: 4 additions & 10 deletions src/ucs/sys/topo.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
33 changes: 33 additions & 0 deletions test/gtest/ucs/test_string.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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)));
Expand Down

0 comments on commit 10b7fc2

Please sign in to comment.