[fuzzing] Improve OSA distance performance (#33149)

Early out evaluating this function where we can, and use macros to
eliminate function calls in debug builds.

Takes per-example time from 5400ms to 1200ms in debug asan builds.

<!--

If you know who should review your pull request, please assign it to
that
person, otherwise the pull request would get assigned randomly.

If your pull request is for a specific language, please add the
appropriate
lang label.

-->

---------

Co-authored-by: ctiller <ctiller@users.noreply.github.com>
revert-33154-merge-fest
Craig Tiller 2 years ago committed by GitHub
parent cc3d034948
commit 5fac4ad47b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 44
      test/core/end2end/end2end_test_fuzzer.cc
  2. 41
      test/core/util/osa_distance.cc

@ -16,6 +16,7 @@
#include <algorithm> #include <algorithm>
#include <chrono> #include <chrono>
#include <limits>
#include <memory> #include <memory>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
@ -68,6 +69,10 @@ DEFINE_PROTO_FUZZER(const core_end2end_test_fuzzer::Msg& msg) {
factory; factory;
}; };
static const auto only_suite = grpc_core::GetEnv("GRPC_TEST_FUZZER_SUITE");
static const auto only_test = grpc_core::GetEnv("GRPC_TEST_FUZZER_TEST");
static const auto only_config = grpc_core::GetEnv("GRPC_TEST_FUZZER_CONFIG");
static const auto all_tests = static const auto all_tests =
grpc_core::CoreEnd2endTestRegistry::Get().AllTests(); grpc_core::CoreEnd2endTestRegistry::Get().AllTests();
static const auto tests = []() { static const auto tests = []() {
@ -76,9 +81,6 @@ DEFINE_PROTO_FUZZER(const core_end2end_test_fuzzer::Msg& msg) {
grpc_core::ForceEnableExperiment("event_engine_client", true); grpc_core::ForceEnableExperiment("event_engine_client", true);
grpc_core::ForceEnableExperiment("event_engine_listener", true); grpc_core::ForceEnableExperiment("event_engine_listener", true);
auto only_suite = grpc_core::GetEnv("GRPC_TEST_FUZZER_SUITE");
auto only_test = grpc_core::GetEnv("GRPC_TEST_FUZZER_TEST");
auto only_config = grpc_core::GetEnv("GRPC_TEST_FUZZER_CONFIG");
std::vector<Test> tests; std::vector<Test> tests;
for (const auto& test : all_tests) { for (const auto& test : all_tests) {
if (test.config->feature_mask & FEATURE_MASK_DO_NOT_FUZZ) continue; if (test.config->feature_mask & FEATURE_MASK_DO_NOT_FUZZ) continue;
@ -87,14 +89,17 @@ DEFINE_PROTO_FUZZER(const core_end2end_test_fuzzer::Msg& msg) {
if (only_config.has_value() && test.config->name != only_config.value()) { if (only_config.has_value() && test.config->name != only_config.value()) {
continue; continue;
} }
std::string test_name =
absl::StrCat(test.suite, ".", test.name, "/", test.config->name);
tests.emplace_back( tests.emplace_back(
Test{absl::StrCat(test.suite, ".", test.name, "/", test.config->name), Test{std::move(test_name), [&test]() {
[&test]() {
return std::unique_ptr<grpc_core::CoreEnd2endTest>( return std::unique_ptr<grpc_core::CoreEnd2endTest>(
test.make_test(test.config)); test.make_test(test.config));
}}); }});
} }
GPR_ASSERT(!tests.empty()); GPR_ASSERT(!tests.empty());
std::sort(tests.begin(), tests.end(),
[](const Test& a, const Test& b) { return a.name < b.name; });
return tests; return tests;
}(); }();
static const auto only_experiment = static const auto only_experiment =
@ -106,13 +111,30 @@ DEFINE_PROTO_FUZZER(const core_end2end_test_fuzzer::Msg& msg) {
auto test_name = auto test_name =
absl::StrCat(msg.suite(), ".", msg.test(), "/", msg.config()); absl::StrCat(msg.suite(), ".", msg.test(), "/", msg.config());
size_t best_test = 0; auto it = std::lower_bound(
for (size_t i = 0; i < tests.size(); i++) { tests.begin(), tests.end(), test_name,
if (grpc_core::OsaDistance(test_name, tests[i].name) < [](const Test& a, absl::string_view b) { return a.name < b; });
grpc_core::OsaDistance(test_name, tests[best_test].name)) { if (only_suite.has_value() || only_test.has_value() ||
best_test = i; only_config.has_value()) {
// We get faster convergence for selective tests if we do a fuzzy match
// instead of an exact one. The opposite is true for non-selective tests.
if (it == tests.end() || it->name != test_name) {
size_t best_test = 0;
size_t best_distance = std::numeric_limits<size_t>::max();
for (size_t i = 0; i < tests.size(); i++) {
auto distance = grpc_core::OsaDistance(test_name, tests[i].name);
if (distance < best_distance) {
best_test = i;
best_distance = distance;
if (distance == 0) break;
}
}
it = tests.begin() + best_test;
} }
} }
if (it == tests.end()) {
return;
}
if (only_experiment.has_value() && if (only_experiment.has_value() &&
msg.config_vars().experiments() != only_experiment.value()) { msg.config_vars().experiments() != only_experiment.value()) {
@ -134,7 +156,7 @@ DEFINE_PROTO_FUZZER(const core_end2end_test_fuzzer::Msg& msg) {
auto engine = auto engine =
std::dynamic_pointer_cast<FuzzingEventEngine>(GetDefaultEventEngine()); std::dynamic_pointer_cast<FuzzingEventEngine>(GetDefaultEventEngine());
auto test = tests[best_test].factory(); auto test = it->factory();
test->SetCrashOnStepFailure(); test->SetCrashOnStepFailure();
test->SetQuiesceEventEngine( test->SetQuiesceEventEngine(
[](std::shared_ptr<grpc_event_engine::experimental::EventEngine>&& ee) { [](std::shared_ptr<grpc_event_engine::experimental::EventEngine>&& ee) {

@ -23,47 +23,38 @@
namespace grpc_core { namespace grpc_core {
namespace {
class Matrix {
public:
Matrix(size_t width, size_t height)
: width_(width),
data_(width * height, std::numeric_limits<size_t>::max()) {}
size_t& operator()(size_t x, size_t y) { return data_[y * width_ + x]; }
private:
size_t width_;
std::vector<size_t> data_;
};
} // namespace
size_t OsaDistance(absl::string_view s1, absl::string_view s2) { size_t OsaDistance(absl::string_view s1, absl::string_view s2) {
if (s1.size() > s2.size()) std::swap(s1, s2); if (s1.size() > s2.size()) std::swap(s1, s2);
if (s1.empty()) return static_cast<uint8_t>(s2.size()); if (s1.empty()) return static_cast<uint8_t>(s2.size());
Matrix d(s1.size() + 1, s2.size() + 1); const auto width = s1.size() + 1;
d(0, 0) = 0; const auto height = s2.size() + 1;
std::vector<size_t> matrix(width * height,
std::numeric_limits<size_t>::max());
#define MATRIX_CELL(x, y) matrix[(y)*width + (x)]
MATRIX_CELL(0, 0) = 0;
for (size_t i = 1; i <= s1.size(); ++i) { for (size_t i = 1; i <= s1.size(); ++i) {
d(i, 0) = i; MATRIX_CELL(i, 0) = i;
} }
for (size_t j = 1; j <= s2.size(); ++j) { for (size_t j = 1; j <= s2.size(); ++j) {
d(0, j) = j; MATRIX_CELL(0, j) = j;
} }
for (size_t i = 1; i <= s1.size(); ++i) { for (size_t i = 1; i <= s1.size(); ++i) {
for (size_t j = 1; j <= s2.size(); ++j) { for (size_t j = 1; j <= s2.size(); ++j) {
const size_t cost = s1[i - 1] == s2[j - 1] ? 0 : 1; const size_t cost = s1[i - 1] == s2[j - 1] ? 0 : 1;
d(i, j) = std::min({ MATRIX_CELL(i, j) = std::min({
d(i - 1, j) + 1, // deletion MATRIX_CELL(i - 1, j) + 1, // deletion
d(i, j - 1) + 1, // insertion MATRIX_CELL(i, j - 1) + 1, // insertion
d(i - 1, j - 1) + cost // substitution MATRIX_CELL(i - 1, j - 1) + cost // substitution
}); });
if (i > 1 && j > 1 && s1[i - 1] == s2[j - 2] && s1[i - 2] == s2[j - 1]) { if (i > 1 && j > 1 && s1[i - 1] == s2[j - 2] && s1[i - 2] == s2[j - 1]) {
d(i, j) = std::min(d(i, j), d(i - 2, j - 2) + 1); // transposition MATRIX_CELL(i, j) = std::min(
MATRIX_CELL(i, j), MATRIX_CELL(i - 2, j - 2) + 1); // transposition
} }
} }
} }
return d(s1.size(), s2.size()); return MATRIX_CELL(s1.size(), s2.size());
} }
} // namespace grpc_core } // namespace grpc_core

Loading…
Cancel
Save