Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update cost model for shortest path #1499

Merged
merged 1 commit into from
Apr 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/include/planner/join_order/cardinality_estimator.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#pragma once

#include "binder/query/reading_clause/query_graph.h"
#include "planner/logical_plan/logical_plan.h"
#include "storage/store/nodes_statistics_and_deleted_ids.h"
Expand Down
16 changes: 16 additions & 0 deletions src/include/planner/join_order/join_order_util.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

#include "planner/logical_plan/logical_plan.h"

namespace kuzu {
namespace planner {

struct JoinOrderUtil {
// Although we do not flatten join key in Build operator computation. We still need to perform
// cardinality and cost estimation based on their flat cardinality.
static uint64_t getJoinKeysFlatCardinality(
const binder::expression_vector& joinNodeIDs, const LogicalPlan& buildPlan);
};

} // namespace planner
} // namespace kuzu
6 changes: 3 additions & 3 deletions src/optimizer/filter_push_down_optimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,6 @@ std::shared_ptr<planner::LogicalOperator> FilterPushDownOptimizer::visitScanNode
}
properties.push_back(property);
}
if (properties.empty()) {
return currentRoot;
}
return appendScanNodeProperty(node, properties, currentRoot);
}

Expand Down Expand Up @@ -184,6 +181,9 @@ std::shared_ptr<planner::LogicalOperator> FilterPushDownOptimizer::finishPushDow
std::shared_ptr<planner::LogicalOperator> FilterPushDownOptimizer::appendScanNodeProperty(
std::shared_ptr<binder::NodeExpression> node, binder::expression_vector properties,
std::shared_ptr<planner::LogicalOperator> child) {
if (properties.empty()) {
return child;
}
auto scanNodeProperty = std::make_shared<LogicalScanNodeProperty>(
std::move(node), std::move(properties), std::move(child));
scanNodeProperty->computeFlatSchema();
Expand Down
3 changes: 2 additions & 1 deletion src/planner/join_order/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
add_library(kuzu_planner_join_order
OBJECT
cardinality_estimator.cpp
cost_model.cpp)
cost_model.cpp
join_order_util.cpp)

set(ALL_OBJECT_FILES
${ALL_OBJECT_FILES} $<TARGET_OBJECTS:kuzu_planner_join_order>
Expand Down
25 changes: 4 additions & 21 deletions src/planner/join_order/cardinality_estimator.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "planner/join_order/cardinality_estimator.h"

#include "planner/join_order/join_order_util.h"
#include "planner/logical_plan/logical_operator/logical_extend.h"
#include "planner/logical_plan/logical_operator/logical_scan_node.h"

Expand All @@ -20,33 +21,15 @@ uint64_t CardinalityEstimator::estimateScanNode(LogicalOperator* op) {
return atLeastOne(getNodeIDDom(scanNode->getNode()->getInternalIDPropertyName()));
}

// Although we may not flatten join key in Build operator computation. We do need to calculate join
// cardinality based on flat join key cardinality.
static uint64_t getJoinKeysFlatCardinality(
const binder::expression_vector& joinNodeIDs, const LogicalPlan& buildPlan) {
auto schema = buildPlan.getSchema();
f_group_pos_set unFlatGroupsPos;
for (auto& joinID : joinNodeIDs) {
auto groupPos = schema->getGroupPos(*joinID);
if (!schema->getGroup(groupPos)->isFlat()) {
unFlatGroupsPos.insert(groupPos);
}
}
auto cardinality = buildPlan.getCardinality();
for (auto groupPos : unFlatGroupsPos) {
cardinality *= schema->getGroup(groupPos)->getMultiplier();
}
return cardinality;
}

uint64_t CardinalityEstimator::estimateHashJoin(const binder::expression_vector& joinNodeIDs,
const LogicalPlan& probePlan, const LogicalPlan& buildPlan) {
auto denominator = 1u;
for (auto& joinNodeID : joinNodeIDs) {
denominator *= getNodeIDDom(joinNodeID->getUniqueName());
}
return atLeastOne(probePlan.estCardinality *
getJoinKeysFlatCardinality(joinNodeIDs, buildPlan) / denominator);
JoinOrderUtil::getJoinKeysFlatCardinality(joinNodeIDs, buildPlan) /
denominator);
}

uint64_t CardinalityEstimator::estimateCrossProduct(
Expand Down Expand Up @@ -128,7 +111,7 @@ double CardinalityEstimator::getExtensionRate(
}
case common::QueryRelType::VARIABLE_LENGTH:
case common::QueryRelType::SHORTEST: {
return oneHopExtensionRate * 2 /*magic number*/;
return std::min<double>(oneHopExtensionRate * rel.getUpperBound(), numRels);
}
default:
throw common::NotImplementedException("getExtensionRate()");
Expand Down
8 changes: 5 additions & 3 deletions src/planner/join_order/cost_model.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "planner/join_order/cost_model.h"

#include "common/constants.h"
#include "planner/join_order/join_order_util.h"

namespace kuzu {
namespace planner {
Expand All @@ -17,11 +18,12 @@ uint64_t CostModel::computeRecursiveExtendCost(

uint64_t CostModel::computeHashJoinCost(const binder::expression_vector& joinNodeIDs,
const LogicalPlan& probe, const LogicalPlan& build) {
auto cost = 0u;
auto cost = 0ul;
cost += probe.getCost();
cost += build.getCost();
cost += probe.getCardinality();
cost += common::PlannerKnobs::BUILD_PENALTY * build.getCardinality();
cost += common::PlannerKnobs::BUILD_PENALTY *
JoinOrderUtil::getJoinKeysFlatCardinality(joinNodeIDs, build);
return cost;
}

Expand All @@ -32,7 +34,7 @@ uint64_t CostModel::computeMarkJoinCost(const binder::expression_vector& joinNod

uint64_t CostModel::computeIntersectCost(const kuzu::planner::LogicalPlan& probePlan,
const std::vector<std::unique_ptr<LogicalPlan>>& buildPlans) {
auto cost = 0u;
auto cost = 0ul;
cost += probePlan.getCost();
// TODO(Xiyang): think of how to calculate intersect cost such that it will be picked in worst
// case.
Expand Down
24 changes: 24 additions & 0 deletions src/planner/join_order/join_order_util.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include "planner/join_order/join_order_util.h"

namespace kuzu {
namespace planner {

uint64_t JoinOrderUtil::getJoinKeysFlatCardinality(
const binder::expression_vector& joinNodeIDs, const LogicalPlan& buildPlan) {
auto schema = buildPlan.getSchema();
f_group_pos_set unFlatGroupsPos;
for (auto& joinID : joinNodeIDs) {
auto groupPos = schema->getGroupPos(*joinID);
if (!schema->getGroup(groupPos)->isFlat()) {
unFlatGroupsPos.insert(groupPos);
}
}
auto cardinality = buildPlan.getCardinality();
for (auto groupPos : unFlatGroupsPos) {
cardinality *= schema->getGroup(groupPos)->getMultiplier();
}
return cardinality;
}

} // namespace planner
} // namespace kuzu
2 changes: 1 addition & 1 deletion test/optimizer/optimizer_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ TEST_F(OptimizerTest, JoinOrderTest3) {
TEST_F(OptimizerTest, RecursiveJoinTest) {
auto encodedPlan = getEncodedPlan(
"MATCH (a:person)-[:knows* SHORTEST 1..5]->(b:person) WHERE b.ID < 0 RETURN a.fName;");
ASSERT_STREQ(encodedPlan.c_str(), "HJ(a._id){S(a)}{RE(a)S(b)}");
ASSERT_STREQ(encodedPlan.c_str(), "HJ(a._id){RE(a)S(b)}{S(a)}");
}

} // namespace testing
Expand Down