-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
utlity: Add benchmark for string-utility functions (#2348)
Adds a benchmarking for new string utility functions added in #2368 using the google microbenchmarking library (https://github.com/google/benchmark), which was added in #2350. This serves as an example and proof of concept on how to use the benchmarking library, and as a reference point for the approximate performance of some string-parsing functions. Risk Level: Very Low Testing: This is just a new speed-test, so running it on its own is sufficient to test it. Results: 2018-01-18 08:01:44 Run on (12 X 3800 MHz CPU s) CPU Caches: L1 Data 32K (x6) L1 Instruction 32K (x6) L2 Unified 256K (x6) L3 Unified 15360K (x1) ***WARNING*** CPU scaling is enabled, the benchmark real time measurements may be noisy and will incur extra overhead. ------------------------------------------------------------------------------------- Benchmark Time CPU Iterations ------------------------------------------------------------------------------------- BM_RTrimStringView 11 ns 11 ns 54467092 BM_RTrimStringViewAlreadyTrimmed 9 ns 9 ns 77143046 BM_RTrimStringViewAlreadyTrimmedAndMakeString 24 ns 24 ns 29094696 BM_FindToken 165 ns 165 ns 4230849 BM_FindTokenValueNestedSplit 427 ns 427 ns 1653627 BM_FindTokenValueSearchForEqual 180 ns 180 ns 4046401 In practice I did not find this benchmark to be noisy on repeated runs. Signed-off-by: Joshua Marantz <[email protected]>
- Loading branch information
Showing
3 changed files
with
172 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
// Note: this should be run with --compilation_mode=opt, and would benefit from a | ||
// quiescent system with disabled cstate power management. | ||
|
||
#include "common/common/assert.h" | ||
#include "common/common/utility.h" | ||
|
||
#include "absl/strings/string_view.h" | ||
#include "testing/base/public/benchmark.h" | ||
|
||
static const char TextToTrim[] = "\t the quick brown fox jumps over the lazy dog\n\r\n"; | ||
static size_t TextToTrimLength = sizeof(TextToTrim) - 1; | ||
|
||
static const char AlreadyTrimmed[] = "the quick brown fox jumps over the lazy dog"; | ||
static size_t AlreadyTrimmedLength = sizeof(AlreadyTrimmed) - 1; | ||
|
||
static const char CacheControl[] = "private, max-age=300, no-transform"; | ||
static size_t CacheControlLength = sizeof(CacheControl) - 1; | ||
|
||
// NOLINT(namespace-envoy) | ||
|
||
static void BM_RTrimStringView(benchmark::State& state) { | ||
int accum = 0; | ||
for (auto _ : state) { | ||
absl::string_view text(TextToTrim, TextToTrimLength); | ||
text = Envoy::StringUtil::rtrim(text); | ||
accum += TextToTrimLength - text.size(); | ||
} | ||
benchmark::DoNotOptimize(accum); | ||
} | ||
BENCHMARK(BM_RTrimStringView); | ||
|
||
static void BM_RTrimStringViewAlreadyTrimmed(benchmark::State& state) { | ||
int accum = 0; | ||
for (auto _ : state) { | ||
absl::string_view text(AlreadyTrimmed, AlreadyTrimmedLength); | ||
text = Envoy::StringUtil::rtrim(text); | ||
accum += AlreadyTrimmedLength - text.size(); | ||
} | ||
benchmark::DoNotOptimize(accum); | ||
} | ||
BENCHMARK(BM_RTrimStringViewAlreadyTrimmed); | ||
|
||
static void BM_RTrimStringViewAlreadyTrimmedAndMakeString(benchmark::State& state) { | ||
int accum = 0; | ||
for (auto _ : state) { | ||
absl::string_view text(AlreadyTrimmed, AlreadyTrimmedLength); | ||
std::string string_copy = std::string(Envoy::StringUtil::rtrim(text)); | ||
accum += AlreadyTrimmedLength - string_copy.size(); | ||
} | ||
benchmark::DoNotOptimize(accum); | ||
} | ||
BENCHMARK(BM_RTrimStringViewAlreadyTrimmedAndMakeString); | ||
|
||
static void BM_FindToken(benchmark::State& state) { | ||
const absl::string_view cache_control(CacheControl, CacheControlLength); | ||
for (auto _ : state) { | ||
RELEASE_ASSERT(Envoy::StringUtil::findToken(cache_control, ",", "no-transform")); | ||
} | ||
} | ||
BENCHMARK(BM_FindToken); | ||
|
||
static bool nextToken(absl::string_view& str, char delim, bool stripWhitespace, | ||
absl::string_view* token) { | ||
while (!str.empty()) { | ||
absl::string_view::size_type pos = str.find(delim); | ||
if (pos == absl::string_view::npos) { | ||
*token = str.substr(0, str.size()); | ||
str.remove_prefix(str.size()); // clears str | ||
} else { | ||
*token = str.substr(0, pos); | ||
str.remove_prefix(pos + 1); // move past token and delim | ||
} | ||
if (stripWhitespace) { | ||
*token = Envoy::StringUtil::trim(*token); | ||
} | ||
if (!token->empty()) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
// Experimental alternative implementation of StringUtil::findToken which doesn't create | ||
// a temp vector, but just iterates through the string_view, tokenizing, and matching against | ||
// the token we want. It appears to be about 2.5x to 3x faster on this testcase. | ||
static bool findTokenWithoutSplitting(absl::string_view str, char delim, absl::string_view token, | ||
bool stripWhitespace) { | ||
for (absl::string_view tok; nextToken(str, delim, stripWhitespace, &tok);) { | ||
if (tok == token) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
static void BM_FindTokenWithoutSplitting(benchmark::State& state) { | ||
const absl::string_view cache_control(CacheControl, CacheControlLength); | ||
for (auto _ : state) { | ||
RELEASE_ASSERT(findTokenWithoutSplitting(cache_control, ',', "no-transform", true)); | ||
} | ||
} | ||
BENCHMARK(BM_FindTokenWithoutSplitting); | ||
|
||
static void BM_FindTokenValueNestedSplit(benchmark::State& state) { | ||
const absl::string_view cache_control(CacheControl, CacheControlLength); | ||
absl::string_view max_age; | ||
for (auto _ : state) { | ||
for (absl::string_view token : Envoy::StringUtil::splitToken(cache_control, ",")) { | ||
auto name_value = Envoy::StringUtil::splitToken(token, "="); | ||
if ((name_value.size() == 2) && (Envoy::StringUtil::trim(name_value[0]) == "max-age")) { | ||
max_age = Envoy::StringUtil::trim(name_value[1]); | ||
} | ||
} | ||
RELEASE_ASSERT(max_age == "300"); | ||
} | ||
} | ||
BENCHMARK(BM_FindTokenValueNestedSplit); | ||
|
||
static void BM_FindTokenValueSearchForEqual(benchmark::State& state) { | ||
for (auto _ : state) { | ||
const absl::string_view cache_control(CacheControl, CacheControlLength); | ||
absl::string_view max_age; | ||
for (absl::string_view token : Envoy::StringUtil::splitToken(cache_control, ",")) { | ||
absl::string_view::size_type equals = token.find('='); | ||
if (equals != absl::string_view::npos && | ||
Envoy::StringUtil::trim(token.substr(0, equals)) == "max-age") { | ||
max_age = Envoy::StringUtil::trim(token.substr(equals + 1)); | ||
} | ||
} | ||
RELEASE_ASSERT(max_age == "300"); | ||
} | ||
} | ||
BENCHMARK(BM_FindTokenValueSearchForEqual); | ||
|
||
static void BM_FindTokenValueNoSplit(benchmark::State& state) { | ||
for (auto _ : state) { | ||
absl::string_view cache_control(CacheControl, CacheControlLength); | ||
absl::string_view max_age; | ||
for (absl::string_view token; nextToken(cache_control, ',', true, &token);) { | ||
absl::string_view name; | ||
if (nextToken(token, '=', true, &name) && (name == "max-age")) { | ||
max_age = Envoy::StringUtil::trim(token); | ||
} | ||
} | ||
RELEASE_ASSERT(max_age == "300"); | ||
} | ||
} | ||
BENCHMARK(BM_FindTokenValueNoSplit); | ||
|
||
// Boilerplate main(), which discovers benchmarks in the same file and runs them. | ||
int main(int argc, char** argv) { | ||
benchmark::Initialize(&argc, argv); | ||
if (benchmark::ReportUnrecognizedArguments(argc, argv)) { | ||
return 1; | ||
} | ||
benchmark::RunSpecifiedBenchmarks(); | ||
} |