From 7fe19e2b100c9942889aece496580f9170b404ea Mon Sep 17 00:00:00 2001 From: HÃ¥vard Pettersen Date: Mon, 31 Oct 2022 14:37:01 +0000 Subject: short-circuit filter evaluation if filter iterator is trivial --- .../queryeval/global_filter/global_filter_test.cpp | 18 +++++++ .../vespa/searchlib/queryeval/global_filter.cpp | 55 +++++++++++++++++----- 2 files changed, 61 insertions(+), 12 deletions(-) diff --git a/searchlib/src/tests/queryeval/global_filter/global_filter_test.cpp b/searchlib/src/tests/queryeval/global_filter/global_filter_test.cpp index a18fe010ad0..dcf175a6fdd 100644 --- a/searchlib/src/tests/queryeval/global_filter/global_filter_test.cpp +++ b/searchlib/src/tests/queryeval/global_filter/global_filter_test.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -199,4 +200,21 @@ TEST(GlobalFilterTest, multi_threaded_global_filter_works_with_docid_limit_0) { verify(*filter, 2, 1); } +TEST(GlobalFilterTest, global_filter_matching_any_document_becomes_invalid) { + SimpleThreadBundle thread_bundle(7); + AlwaysTrueBlueprint blueprint; + auto filter = GlobalFilter::create(blueprint, 100, thread_bundle); + EXPECT_FALSE(filter->is_active()); +} + +TEST(GlobalFilterTest, global_filter_not_matching_any_document_becomes_empty) { + SimpleThreadBundle thread_bundle(7); + EmptyBlueprint blueprint; + auto filter = GlobalFilter::create(blueprint, 100, thread_bundle); + auto class_name = vespalib::getClassName(*filter); + fprintf(stderr, "empty global filter class name: %s\n", class_name.c_str()); + EXPECT_TRUE(class_name.find("EmptyFilter") < class_name.size()); + verify(*filter, 1000, 100); +} + GTEST_MAIN_RUN_ALL_TESTS() diff --git a/searchlib/src/vespa/searchlib/queryeval/global_filter.cpp b/searchlib/src/vespa/searchlib/queryeval/global_filter.cpp index 04e4d154821..3ef28b461b8 100644 --- a/searchlib/src/vespa/searchlib/queryeval/global_filter.cpp +++ b/searchlib/src/vespa/searchlib/queryeval/global_filter.cpp @@ -9,6 +9,7 @@ using vespalib::Runnable; using vespalib::ThreadBundle; +using vespalib::Trinary; namespace search::queryeval { @@ -21,6 +22,15 @@ struct Inactive : GlobalFilter { bool check(uint32_t) const override { abort(); } }; +struct EmptyFilter : GlobalFilter { + uint32_t docid_limit; + EmptyFilter(uint32_t docid_limit_in) : docid_limit(docid_limit_in) {} + bool is_active() const override { return true; } + uint32_t size() const override { return docid_limit; } + uint32_t count() const override { return 0; } + bool check(uint32_t) const override { return false; } +}; + struct BitVectorFilter : public GlobalFilter { std::unique_ptr vector; BitVectorFilter(std::unique_ptr vector_in) @@ -56,22 +66,38 @@ struct MultiBitVectorFilter : public GlobalFilter { } }; -std::unique_ptr make_part(Blueprint &blueprint, uint32_t begin, uint32_t end) { +struct PartResult { + Trinary matches_any; + std::unique_ptr bits; + PartResult() + : matches_any(Trinary::False), bits() {} + PartResult(Trinary matches_any_in) + : matches_any(matches_any_in), bits() {} + PartResult(std::unique_ptr &&bits_in) + : matches_any(Trinary::Undefined), bits(std::move(bits_in)) {} +}; + +PartResult make_part(Blueprint &blueprint, uint32_t begin, uint32_t end) { bool strict = true; auto constraint = Blueprint::FilterConstraint::UPPER_BOUND; - auto filter_iterator = blueprint.createFilterSearch(strict, constraint); - filter_iterator->initRange(begin, end); - auto result = filter_iterator->get_hits(begin); - // count bits in parallel and cache the results for later - result->countTrueBits(); - return result; + auto filter = blueprint.createFilterSearch(strict, constraint); + auto matches_any = filter->matches_any(); + if (matches_any == Trinary::Undefined) { + filter->initRange(begin, end); + auto bits = filter->get_hits(begin); + // count bits in parallel and cache the results for later + bits->countTrueBits(); + return PartResult(std::move(bits)); + } else { + return PartResult(matches_any); + } } struct MakePart : Runnable { Blueprint &blueprint; uint32_t begin; uint32_t end; - std::unique_ptr result; + PartResult result; MakePart(Blueprint &blueprint_in, uint32_t begin_in, uint32_t end_in) noexcept : blueprint(blueprint_in), begin(begin_in), end(end_in), result() {} void run() override { result = make_part(blueprint, begin, end); } @@ -146,13 +172,18 @@ GlobalFilter::create(Blueprint &blueprint, uint32_t docid_limit, ThreadBundle &t assert(parts.size() <= num_threads); assert((docid == docid_limit) || parts.empty()); thread_bundle.run(parts); - if (parts.size() == 1) { - return create(std::move(parts[0].result)); - } std::vector> vectors; vectors.reserve(parts.size()); for (MakePart &part: parts) { - vectors.push_back(std::move(part.result)); + switch (part.result.matches_any) { + case Trinary::False: return std::make_unique(docid_limit); + case Trinary::True: return create(); // filter not needed after all + case Trinary::Undefined: + vectors.push_back(std::move(part.result.bits)); + } + } + if (vectors.size() == 1) { + return create(std::move(vectors[0])); } return create(std::move(vectors)); } -- cgit v1.2.3