Speed up field trial parser for large inputs

Change search for next separator to be linear in length of the string
(instead of potentially quadratic)
Reduce copying of std::string by switch to string_view
Throttle logging about unknown key.

Bug: b/204541739
Change-Id: I81d5cd4432966a0a5808077f9001bc62960e5e60
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/237500
Reviewed-by: Sebastian Jansson <srte@webrtc.org>
Reviewed-by: Fanny Linderborg <linderborg@webrtc.org>
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#35330}
This commit is contained in:
Danil Chapovalov
2021-11-09 11:41:54 +01:00
committed by WebRTC LUCI CQ
parent 7de81e2717
commit 1b320f8b7d
2 changed files with 38 additions and 30 deletions

View File

@ -16,19 +16,12 @@
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include "absl/strings/string_view.h"
#include "rtc_base/checks.h" #include "rtc_base/checks.h"
#include "rtc_base/logging.h" #include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_conversions.h" #include "rtc_base/numerics/safe_conversions.h"
namespace webrtc { namespace webrtc {
namespace {
int FindOrEnd(std::string str, size_t start, char delimiter) {
size_t pos = str.find(delimiter, start);
pos = (pos == std::string::npos) ? str.length() : pos;
return static_cast<int>(pos);
}
} // namespace
FieldTrialParameterInterface::FieldTrialParameterInterface(std::string key) FieldTrialParameterInterface::FieldTrialParameterInterface(std::string key)
: key_(key) {} : key_(key) {}
@ -39,8 +32,8 @@ FieldTrialParameterInterface::~FieldTrialParameterInterface() {
void ParseFieldTrial( void ParseFieldTrial(
std::initializer_list<FieldTrialParameterInterface*> fields, std::initializer_list<FieldTrialParameterInterface*> fields,
std::string trial_string) { absl::string_view trial_string) {
std::map<std::string, FieldTrialParameterInterface*> field_map; std::map<absl::string_view, FieldTrialParameterInterface*> field_map;
FieldTrialParameterInterface* keyless_field = nullptr; FieldTrialParameterInterface* keyless_field = nullptr;
for (FieldTrialParameterInterface* field : fields) { for (FieldTrialParameterInterface* field : fields) {
field->MarkAsUsed(); field->MarkAsUsed();
@ -60,18 +53,29 @@ void ParseFieldTrial(
field_map[field->key_] = field; field_map[field->key_] = field;
} }
} }
bool logged_unknown_key = false;
size_t i = 0; absl::string_view tail = trial_string;
while (i < trial_string.length()) { while (!tail.empty()) {
int val_end = FindOrEnd(trial_string, i, ','); size_t key_end = tail.find_first_of(",:");
int colon_pos = FindOrEnd(trial_string, i, ':'); absl::string_view key = tail.substr(0, key_end);
int key_end = std::min(val_end, colon_pos);
int val_begin = key_end + 1;
std::string key = trial_string.substr(i, key_end - i);
absl::optional<std::string> opt_value; absl::optional<std::string> opt_value;
if (val_end >= val_begin) if (key_end == absl::string_view::npos) {
opt_value = trial_string.substr(val_begin, val_end - val_begin); tail = "";
i = val_end + 1; } else if (tail[key_end] == ':') {
tail = tail.substr(key_end + 1);
size_t value_end = tail.find(',');
opt_value.emplace(tail.substr(0, value_end));
if (value_end == absl::string_view::npos) {
tail = "";
} else {
tail = tail.substr(value_end + 1);
}
} else {
RTC_DCHECK_EQ(tail[key_end], ',');
tail = tail.substr(key_end + 1);
}
auto field = field_map.find(key); auto field = field_map.find(key);
if (field != field_map.end()) { if (field != field_map.end()) {
if (!field->second->Parse(std::move(opt_value))) { if (!field->second->Parse(std::move(opt_value))) {
@ -79,7 +83,7 @@ void ParseFieldTrial(
<< "' in trial: \"" << trial_string << "\""; << "' in trial: \"" << trial_string << "\"";
} }
} else if (!opt_value && keyless_field && !key.empty()) { } else if (!opt_value && keyless_field && !key.empty()) {
if (!keyless_field->Parse(key)) { if (!keyless_field->Parse(std::string(key))) {
RTC_LOG(LS_WARNING) << "Failed to read empty key field with value '" RTC_LOG(LS_WARNING) << "Failed to read empty key field with value '"
<< key << "' in trial: \"" << trial_string << "\""; << key << "' in trial: \"" << trial_string << "\"";
} }
@ -87,14 +91,17 @@ void ParseFieldTrial(
// "_" is be used to prefix keys that are part of the string for // "_" is be used to prefix keys that are part of the string for
// debugging purposes but not neccessarily used. // debugging purposes but not neccessarily used.
// e.g. WebRTC-Experiment/param: value, _DebuggingString // e.g. WebRTC-Experiment/param: value, _DebuggingString
RTC_LOG(LS_INFO) << "No field with key: '" << key if (!logged_unknown_key) {
<< "' (found in trial: \"" << trial_string << "\")"; RTC_LOG(LS_INFO) << "No field with key: '" << key
std::string valid_keys; << "' (found in trial: \"" << trial_string << "\")";
for (const auto& f : field_map) { std::string valid_keys;
valid_keys += f.first; for (const auto& f : field_map) {
valid_keys += ", "; valid_keys.append(f.first.data(), f.first.size());
valid_keys += ", ";
}
RTC_LOG(LS_INFO) << "Valid keys are: " << valid_keys;
logged_unknown_key = true;
} }
RTC_LOG(LS_INFO) << "Valid keys are: " << valid_keys;
} }
} }

View File

@ -18,6 +18,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "absl/strings/string_view.h"
#include "absl/types/optional.h" #include "absl/types/optional.h"
// Field trial parser functionality. Provides funcitonality to parse field trial // Field trial parser functionality. Provides funcitonality to parse field trial
@ -48,7 +49,7 @@ class FieldTrialParameterInterface {
explicit FieldTrialParameterInterface(std::string key); explicit FieldTrialParameterInterface(std::string key);
friend void ParseFieldTrial( friend void ParseFieldTrial(
std::initializer_list<FieldTrialParameterInterface*> fields, std::initializer_list<FieldTrialParameterInterface*> fields,
std::string raw_string); absl::string_view trial_string);
void MarkAsUsed() { used_ = true; } void MarkAsUsed() { used_ = true; }
virtual bool Parse(absl::optional<std::string> str_value) = 0; virtual bool Parse(absl::optional<std::string> str_value) = 0;
@ -65,7 +66,7 @@ class FieldTrialParameterInterface {
// with extracted values if available. // with extracted values if available.
void ParseFieldTrial( void ParseFieldTrial(
std::initializer_list<FieldTrialParameterInterface*> fields, std::initializer_list<FieldTrialParameterInterface*> fields,
std::string raw_string); absl::string_view trial_string);
// Specialize this in code file for custom types. Should return absl::nullopt if // Specialize this in code file for custom types. Should return absl::nullopt if
// the given string cannot be properly parsed. // the given string cannot be properly parsed.