diff --git a/src/query/plan/operator.cpp b/src/query/plan/operator.cpp index fb622f3af..31ec91f30 100644 --- a/src/query/plan/operator.cpp +++ b/src/query/plan/operator.cpp @@ -3042,11 +3042,23 @@ void Unwind::UnwindCursor::Reset() { input_value_it_ = input_value_.end(); } +struct TypedValueVectorAllocatorEqual { + bool operator()( + const std::vector<TypedValue, utils::Allocator<TypedValue>> &left, + const std::vector<TypedValue, utils::Allocator<TypedValue>> &right) + const { + return std::equal(left.begin(), left.end(), right.begin(), right.end(), + TypedValue::BoolEqual()); + } +}; + class DistinctCursor : public Cursor { public: DistinctCursor(const Distinct &self, database::GraphDbAccessor *db, utils::MemoryResource *mem) - : self_(self), input_cursor_(self.input_->MakeCursor(db, mem)) {} + : self_(self), + input_cursor_(self.input_->MakeCursor(db, mem)), + seen_rows_(mem) {} bool Pull(Frame &frame, ExecutionContext &context) override { SCOPED_PROFILE_OP("Distinct"); @@ -3054,7 +3066,8 @@ class DistinctCursor : public Cursor { while (true) { if (!input_cursor_->Pull(frame, context)) return false; - std::vector<TypedValue> row; + std::vector<TypedValue, utils::Allocator<TypedValue>> row( + seen_rows_.get_allocator().GetMemoryResource()); row.reserve(self_.value_symbols_.size()); for (const auto &symbol : self_.value_symbols_) row.emplace_back(frame[symbol]); @@ -3073,12 +3086,14 @@ class DistinctCursor : public Cursor { const Distinct &self_; const std::unique_ptr<Cursor> input_cursor_; // a set of already seen rows - std::unordered_set<std::vector<TypedValue>, - // use FNV collection hashing specialized for a vector of - // TypedValues - utils::FnvCollection<std::vector<TypedValue>, TypedValue, - TypedValue::Hash>, - TypedValueVectorEqual> + std::unordered_set< + std::vector<TypedValue, utils::Allocator<TypedValue>>, + // use FNV collection hashing specialized for a vector of TypedValue + utils::FnvCollection< + std::vector<TypedValue, utils::Allocator<TypedValue>>, TypedValue, + TypedValue::Hash>, + TypedValueVectorAllocatorEqual, + utils::Allocator<std::vector<TypedValue, utils::Allocator<TypedValue>>>> seen_rows_; }; diff --git a/tests/benchmark/CMakeLists.txt b/tests/benchmark/CMakeLists.txt index a11aa5204..256b8843d 100644 --- a/tests/benchmark/CMakeLists.txt +++ b/tests/benchmark/CMakeLists.txt @@ -36,6 +36,9 @@ target_link_libraries(${test_prefix}map_concurrent mg-single-node kvstore_dummy_ add_benchmark(data_structures/ring_buffer.cpp) target_link_libraries(${test_prefix}ring_buffer mg-single-node kvstore_dummy_lib) +add_benchmark(query/execution.cpp) +target_link_libraries(${test_prefix}execution mg-single-node kvstore_dummy_lib) + add_benchmark(query/planner.cpp) target_link_libraries(${test_prefix}planner mg-single-node kvstore_dummy_lib) diff --git a/tests/benchmark/query/execution.cpp b/tests/benchmark/query/execution.cpp new file mode 100644 index 000000000..b0fc61d5b --- /dev/null +++ b/tests/benchmark/query/execution.cpp @@ -0,0 +1,99 @@ +#include <string> + +#include <benchmark/benchmark.h> + +#include "communication/result_stream_faker.hpp" +#include "database/graph_db.hpp" +#include "database/graph_db_accessor.hpp" +#include "query/frontend/opencypher/parser.hpp" +#include "query/frontend/semantic/required_privileges.hpp" +#include "query/frontend/semantic/symbol_generator.hpp" +#include "query/interpreter.hpp" +#include "query/plan/planner.hpp" + +static void AddVertices(database::GraphDb *db, int vertex_count) { + auto dba = db->Access(); + for (int i = 0; i < vertex_count; i++) dba.InsertVertex(); + dba.Commit(); +} + +static query::CypherQuery *ParseCypherQuery(const std::string &query_string, + query::AstStorage *ast) { + query::frontend::ParsingContext parsing_context; + parsing_context.is_query_cached = false; + query::frontend::opencypher::Parser parser(query_string); + // Convert antlr4 AST into Memgraph AST. + query::frontend::CypherMainVisitor cypher_visitor(parsing_context, ast); + cypher_visitor.visit(parser.tree()); + query::Interpreter::ParsedQuery parsed_query{ + cypher_visitor.query(), + query::GetRequiredPrivileges(cypher_visitor.query())}; + return utils::Downcast<query::CypherQuery>(parsed_query.query); +}; + +// NOLINTNEXTLINE(google-runtime-references) +static void DistinctDefaultAllocator(benchmark::State &state) { + query::AstStorage ast; + query::Parameters parameters; + database::GraphDb db; + AddVertices(&db, state.range(0)); + auto dba = db.Access(); + auto query_string = "MATCH (s) RETURN DISTINCT s"; + auto *cypher_query = ParseCypherQuery(query_string, &ast); + auto symbol_table = query::MakeSymbolTable(cypher_query); + auto context = + query::plan::MakePlanningContext(&ast, &symbol_table, cypher_query, &dba); + auto plan_and_cost = + query::plan::MakeLogicalPlan(&context, parameters, false); + ResultStreamFaker<query::TypedValue> results; + query::Frame frame(symbol_table.max_position()); + // Nothing should be used from the EvaluationContext, so leave it empty. + query::EvaluationContext evaluation_context; + while (state.KeepRunning()) { + query::ExecutionContext execution_context{&dba, symbol_table, + evaluation_context}; + auto cursor = plan_and_cost.first->MakeCursor(dba); + while (cursor->Pull(frame, execution_context)) + ; + } + state.SetItemsProcessed(state.iterations()); +} + +BENCHMARK(DistinctDefaultAllocator) + ->Range(1024, 1U << 21U) + ->Unit(benchmark::kMicrosecond); + +// NOLINTNEXTLINE(google-runtime-references) +static void DistinctLinearAllocator(benchmark::State &state) { + query::AstStorage ast; + query::Parameters parameters; + database::GraphDb db; + auto dba = db.Access(); + AddVertices(&db, state.range(0)); + auto query_string = "MATCH (s) RETURN DISTINCT s"; + auto *cypher_query = ParseCypherQuery(query_string, &ast); + auto symbol_table = query::MakeSymbolTable(cypher_query); + auto context = + query::plan::MakePlanningContext(&ast, &symbol_table, cypher_query, &dba); + auto plan_and_cost = + query::plan::MakeLogicalPlan(&context, parameters, false); + ResultStreamFaker<query::TypedValue> results; + query::Frame frame(symbol_table.max_position()); + // Nothing should be used from the EvaluationContext, so leave it empty. + query::EvaluationContext evaluation_context; + while (state.KeepRunning()) { + query::ExecutionContext execution_context{&dba, symbol_table, + evaluation_context}; + utils::MonotonicBufferResource memory(1 * 1024 * 1024); + auto cursor = plan_and_cost.first->MakeCursor(&dba, &memory); + while (cursor->Pull(frame, execution_context)) + ; + } + state.SetItemsProcessed(state.iterations()); +} + +BENCHMARK(DistinctLinearAllocator) + ->Range(1024, 1U << 21U) + ->Unit(benchmark::kMicrosecond); + +BENCHMARK_MAIN();