From dba3010c898d4e590f63eb7018af08efc044303c Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 23 Apr 2021 00:36:39 -0400 Subject: [PATCH] [Strings] Split String Optimizations (#1325) * Switch the 2 split calls to SplitString * Nuke duplicate split in favor of SplitString #1263 * Add a test for SplitString * Optimize SplitString Benchmarking: -------------------------------------------------------------- Benchmark Time CPU Iterations -------------------------------------------------------------- bench_oldsplit 5201 ns 5201 ns 129500 bench_split 1269 ns 1269 ns 548906 This is splitting a VERY long SpecialAbilities string. This is ~75% speed up. --- common/string_util.cpp | 31 +++++++++++-------------------- common/string_util.h | 3 +-- tests/string_util_test.h | 10 ++++++++++ zone/mob_info.cpp | 2 +- zone/zone_event_scheduler.cpp | 2 +- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/common/string_util.cpp b/common/string_util.cpp index f267397c5..6c97dd076 100644 --- a/common/string_util.cpp +++ b/common/string_util.cpp @@ -74,18 +74,6 @@ const std::string str_tolower(std::string s) return s; } -std::vector split(std::string str_to_split, char delimiter) -{ - std::stringstream ss(str_to_split); - std::string item; - std::vector exploded_values; - while (std::getline(ss, item, delimiter)) { - exploded_values.push_back(item); - } - - return exploded_values; -} - const std::string str_toupper(std::string s) { std::transform( @@ -113,15 +101,18 @@ const std::string StringFormat(const char *format, ...) return output; } -std::vector SplitString(const std::string &str, char delim) { +std::vector SplitString(const std::string &str, const char delim) { std::vector ret; - std::stringstream ss(str); - std::string item; - - while(std::getline(ss, item, delim)) { - ret.push_back(item); - } - + std::string::size_type start = 0; + auto end = str.find(delim); + while (end != std::string::npos) { + ret.emplace_back(str, start, end - start); + start = end + 1; + end = str.find(delim, start); + } + // this will catch the last word since the string is unlikely to end with a delimiter + if (str.length() > start) + ret.emplace_back(str, start, str.length() - start); return ret; } diff --git a/common/string_util.h b/common/string_util.h index 31d566bde..5fd91281c 100644 --- a/common/string_util.h +++ b/common/string_util.h @@ -39,7 +39,6 @@ const std::string str_tolower(std::string s); const std::string str_toupper(std::string s); const std::string ucfirst(std::string s); -std::vector split(std::string str_to_split, char delimiter); const std::string StringFormat(const char* format, ...); const std::string vStringFormat(const char* format, va_list args); std::vector wrap(std::vector &src, std::string character); @@ -177,7 +176,7 @@ std::vector join_tuple(const std::string &glue, const std::pair SplitString(const std::string &s, char delim); +std::vector SplitString(const std::string &s, const char delim = ','); std::string::size_type search_deliminated_string(const std::string &haystack, const std::string &needle, const char deliminator = ','); std::string EscapeString(const char *src, size_t sz); std::string EscapeString(const std::string &s); diff --git a/tests/string_util_test.h b/tests/string_util_test.h index 0333d0391..1fdd325f9 100644 --- a/tests/string_util_test.h +++ b/tests/string_util_test.h @@ -30,6 +30,7 @@ public: TEST_ADD(StringUtilTest::EscapeStringTest); TEST_ADD(StringUtilTest::EscapeStringMemoryTest); TEST_ADD(StringUtilTest::SearchDeliminatedStringTest); + TEST_ADD(StringUtilTest::SplitStringTest); } ~StringUtilTest() { @@ -108,6 +109,15 @@ public: TEST_ASSERT(search_deliminated_string(h, "bef") == std::string::npos); TEST_ASSERT(search_deliminated_string(h, "wwi") == std::string::npos); } + + void SplitStringTest() { + std::string s = "123,456,789,"; + auto v = SplitString(s, ','); + TEST_ASSERT(v.size() == 3); + TEST_ASSERT(v[0] == "123"); + TEST_ASSERT(v[1] == "456"); + TEST_ASSERT(v[2] == "789"); + } }; #endif diff --git a/zone/mob_info.cpp b/zone/mob_info.cpp index 554d5d571..5b7011f33 100644 --- a/zone/mob_info.cpp +++ b/zone/mob_info.cpp @@ -557,7 +557,7 @@ inline std::string WriteDisplayInfoSection( * "total_to_hit" = "Total To Hit" */ if (attribute_name.find('_') != std::string::npos) { - std::vector split_string = split(attribute_name, '_'); + auto split_string = SplitString(attribute_name, '_'); std::string new_attribute_name; for (std::string &string_value : split_string) { new_attribute_name += ucfirst(string_value) + " "; diff --git a/zone/zone_event_scheduler.cpp b/zone/zone_event_scheduler.cpp index 657fed78b..4bdb2cd6f 100644 --- a/zone/zone_event_scheduler.cpp +++ b/zone/zone_event_scheduler.cpp @@ -93,7 +93,7 @@ void ZoneEventScheduler::Process(Zone *zone, WorldContentService *content_servic } if (e.event_type == ServerEvents::EVENT_TYPE_RULE_CHANGE) { - auto params = split(e.event_data, '='); + auto params = SplitString(e.event_data, '='); auto rule_key = params[0]; auto rule_value = params[1]; if (!rule_key.empty() && !rule_value.empty()) {