[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>
This commit is contained in:
parent
cc3d034948
commit
5fac4ad47b
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
|
@ -68,6 +69,10 @@ DEFINE_PROTO_FUZZER(const core_end2end_test_fuzzer::Msg& msg) {
|
|||
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 =
|
||||
grpc_core::CoreEnd2endTestRegistry::Get().AllTests();
|
||||
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_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;
|
||||
for (const auto& test : all_tests) {
|
||||
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()) {
|
||||
continue;
|
||||
}
|
||||
std::string test_name =
|
||||
absl::StrCat(test.suite, ".", test.name, "/", test.config->name);
|
||||
tests.emplace_back(
|
||||
Test{absl::StrCat(test.suite, ".", test.name, "/", test.config->name),
|
||||
[&test]() {
|
||||
Test{std::move(test_name), [&test]() {
|
||||
return std::unique_ptr<grpc_core::CoreEnd2endTest>(
|
||||
test.make_test(test.config));
|
||||
}});
|
||||
}
|
||||
GPR_ASSERT(!tests.empty());
|
||||
std::sort(tests.begin(), tests.end(),
|
||||
[](const Test& a, const Test& b) { return a.name < b.name; });
|
||||
return tests;
|
||||
}();
|
||||
static const auto only_experiment =
|
||||
|
|
@ -106,13 +111,30 @@ DEFINE_PROTO_FUZZER(const core_end2end_test_fuzzer::Msg& msg) {
|
|||
|
||||
auto test_name =
|
||||
absl::StrCat(msg.suite(), ".", msg.test(), "/", msg.config());
|
||||
size_t best_test = 0;
|
||||
for (size_t i = 0; i < tests.size(); i++) {
|
||||
if (grpc_core::OsaDistance(test_name, tests[i].name) <
|
||||
grpc_core::OsaDistance(test_name, tests[best_test].name)) {
|
||||
best_test = i;
|
||||
auto it = std::lower_bound(
|
||||
tests.begin(), tests.end(), test_name,
|
||||
[](const Test& a, absl::string_view b) { return a.name < b; });
|
||||
if (only_suite.has_value() || only_test.has_value() ||
|
||||
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() &&
|
||||
msg.config_vars().experiments() != only_experiment.value()) {
|
||||
|
|
@ -134,7 +156,7 @@ DEFINE_PROTO_FUZZER(const core_end2end_test_fuzzer::Msg& msg) {
|
|||
auto engine =
|
||||
std::dynamic_pointer_cast<FuzzingEventEngine>(GetDefaultEventEngine());
|
||||
|
||||
auto test = tests[best_test].factory();
|
||||
auto test = it->factory();
|
||||
test->SetCrashOnStepFailure();
|
||||
test->SetQuiesceEventEngine(
|
||||
[](std::shared_ptr<grpc_event_engine::experimental::EventEngine>&& ee) {
|
||||
|
|
|
|||
|
|
@ -23,47 +23,38 @@
|
|||
|
||||
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) {
|
||||
if (s1.size() > s2.size()) std::swap(s1, s2);
|
||||
if (s1.empty()) return static_cast<uint8_t>(s2.size());
|
||||
|
||||
Matrix d(s1.size() + 1, s2.size() + 1);
|
||||
d(0, 0) = 0;
|
||||
const auto width = s1.size() + 1;
|
||||
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) {
|
||||
d(i, 0) = i;
|
||||
MATRIX_CELL(i, 0) = i;
|
||||
}
|
||||
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 j = 1; j <= s2.size(); ++j) {
|
||||
const size_t cost = s1[i - 1] == s2[j - 1] ? 0 : 1;
|
||||
d(i, j) = std::min({
|
||||
d(i - 1, j) + 1, // deletion
|
||||
d(i, j - 1) + 1, // insertion
|
||||
d(i - 1, j - 1) + cost // substitution
|
||||
MATRIX_CELL(i, j) = std::min({
|
||||
MATRIX_CELL(i - 1, j) + 1, // deletion
|
||||
MATRIX_CELL(i, j - 1) + 1, // insertion
|
||||
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]) {
|
||||
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
|
||||
|
|
|
|||
Loading…
Reference in New Issue