From b3763f7dd27e0916870c315f26a88a8a958b8248 Mon Sep 17 00:00:00 2001 From: Pavel Velikhov Date: Tue, 30 Jan 2024 05:55:44 +0000 Subject: [PATCH] Addressed Alexey's comments, added opt level 2 --- ydb/core/kqp/host/kqp_runner.cpp | 6 ++-- ydb/core/kqp/opt/logical/kqp_opt_cbo.cpp | 33 ++++++++++++++----- ydb/core/kqp/opt/logical/kqp_opt_cbo.h | 13 ++++++-- ydb/core/kqp/opt/logical/kqp_opt_log.cpp | 12 ++++--- ydb/core/kqp/opt/logical/kqp_opt_log.h | 4 ++- .../yql/core/cbo/cbo_optimizer_new.cpp | 5 +-- ydb/library/yql/core/cbo/cbo_optimizer_new.h | 17 +++++----- ydb/library/yql/core/yql_cost_function.cpp | 8 ++--- ydb/library/yql/core/yql_cost_function.h | 8 ++--- .../yql/dq/opt/dq_opt_join_cost_based.cpp | 19 +++++++---- 10 files changed, 81 insertions(+), 44 deletions(-) diff --git a/ydb/core/kqp/host/kqp_runner.cpp b/ydb/core/kqp/host/kqp_runner.cpp index 2d40f257cd92..6db4bb3285be 100644 --- a/ydb/core/kqp/host/kqp_runner.cpp +++ b/ydb/core/kqp/host/kqp_runner.cpp @@ -21,6 +21,8 @@ #include #include +#include + #include namespace NKikimr { @@ -144,7 +146,7 @@ class TKqpRunner : public IKqpRunner { , OptimizeCtx(MakeIntrusive(cluster, Config, sessionCtx->QueryPtr(), sessionCtx->TablesPtr())) , BuildQueryCtx(MakeIntrusive()) - , Pctx(TKqpProviderContext(*OptimizeCtx)) + , Pctx(TKqpProviderContext(*OptimizeCtx, Config->CostBasedOptimizationLevel.Get().GetOrElse(TDqSettings::TDefault::CostBasedOptimizationLevel))) { CreateGraphTransformer(typesCtx, sessionCtx, funcRegistry); } @@ -262,7 +264,7 @@ class TKqpRunner : public IKqpRunner { .AddCommonOptimization() .Add(CreateKqpConstantFoldingTransformer(OptimizeCtx, *typesCtx, Config), "ConstantFolding") .Add(CreateKqpStatisticsTransformer(OptimizeCtx, *typesCtx, Config, Pctx), "Statistics") - .Add(CreateKqpLogOptTransformer(OptimizeCtx, *typesCtx, Config), "LogicalOptimize") + .Add(CreateKqpLogOptTransformer(OptimizeCtx, *typesCtx, Config, Pctx), "LogicalOptimize") .Add(CreateLogicalDataProposalsInspector(*typesCtx), "ProvidersLogicalOptimize") .Add(CreateKqpPhyOptTransformer(OptimizeCtx, *typesCtx), "KqpPhysicalOptimize") .Add(CreatePhysicalDataProposalsInspector(*typesCtx), "ProvidersPhysicalOptimize") diff --git a/ydb/core/kqp/opt/logical/kqp_opt_cbo.cpp b/ydb/core/kqp/opt/logical/kqp_opt_cbo.cpp index a4a5e52e6105..b421ba29757f 100644 --- a/ydb/core/kqp/opt/logical/kqp_opt_cbo.cpp +++ b/ydb/core/kqp/opt/logical/kqp_opt_cbo.cpp @@ -125,22 +125,37 @@ bool IsLookupJoinApplicable(std::shared_ptr left, bool TKqpProviderContext::IsJoinApplicable(const std::shared_ptr& left, const std::shared_ptr& right, const std::set>& joinConditions, - EJoinImplType joinImpl) { + EJoinAlgoType joinAlgo) { - switch( joinImpl ) { - case EJoinImplType::LookupJoin: + switch( joinAlgo ) { + case EJoinAlgoType::LookupJoin: + if (OptLevel==2 && left->Stats->Nrows > 10e3) { + return false; + } return IsLookupJoinApplicable(left, right, joinConditions, *this); - default: + + case EJoinAlgoType::DictJoin: + return right->Stats->Nrows < 10e5; + case EJoinAlgoType::MapJoin: + return right->Stats->Nrows < 10e6; + case EJoinAlgoType::GraceJoin: return true; } } -double TKqpProviderContext::ComputeJoinCost(const TOptimizerStatistics& leftStats, const TOptimizerStatistics& rightStats, EJoinImplType joinImpl) const { +double TKqpProviderContext::ComputeJoinCost(const TOptimizerStatistics& leftStats, const TOptimizerStatistics& rightStats, EJoinAlgoType joinAlgo) const { - switch(joinImpl) { - case EJoinImplType::LookupJoin: - return -1; - default: + switch(joinAlgo) { + case EJoinAlgoType::LookupJoin: + if (OptLevel==1) { + return -1; + } + return leftStats.Nrows; + case EJoinAlgoType::DictJoin: + return leftStats.Nrows + 1.7 * rightStats.Nrows; + case EJoinAlgoType::MapJoin: + return leftStats.Nrows + 1.8 * rightStats.Nrows; + case EJoinAlgoType::GraceJoin: return leftStats.Nrows + 2.0 * rightStats.Nrows; } } diff --git a/ydb/core/kqp/opt/logical/kqp_opt_cbo.h b/ydb/core/kqp/opt/logical/kqp_opt_cbo.h index 95d23db3b4e8..13b6b0200ec5 100644 --- a/ydb/core/kqp/opt/logical/kqp_opt_cbo.h +++ b/ydb/core/kqp/opt/logical/kqp_opt_cbo.h @@ -7,6 +7,9 @@ namespace NKikimr::NKqp::NOpt { +/** + * KQP specific Rel node, includes a pointer to ExprNode +*/ struct TKqpRelOptimizerNode : public NYql::TRelOptimizerNode { const NYql::TExprNode::TPtr Node; @@ -14,17 +17,21 @@ struct TKqpRelOptimizerNode : public NYql::TRelOptimizerNode { TRelOptimizerNode(label, stats), Node(node) { } }; +/** + * KQP Specific cost function and join applicability cost function +*/ struct TKqpProviderContext : public NYql::IProviderContext { - TKqpProviderContext(const TKqpOptimizeContext& kqpCtx) : KqpCtx(kqpCtx) {} + TKqpProviderContext(const TKqpOptimizeContext& kqpCtx, const int optLevel) : KqpCtx(kqpCtx), OptLevel(optLevel) {} virtual bool IsJoinApplicable(const std::shared_ptr& left, const std::shared_ptr& right, const std::set>& joinConditions, - NYql::EJoinImplType joinImpl); + NYql::EJoinAlgoType joinAlgo) override; - virtual double ComputeJoinCost(const NYql::TOptimizerStatistics& leftStats, const NYql::TOptimizerStatistics& rightStats, NYql::EJoinImplType joinImpl) const; + virtual double ComputeJoinCost(const NYql::TOptimizerStatistics& leftStats, const NYql::TOptimizerStatistics& rightStats, NYql::EJoinAlgoType joinAlgo) const override; const TKqpOptimizeContext& KqpCtx; + int OptLevel; }; } \ No newline at end of file diff --git a/ydb/core/kqp/opt/logical/kqp_opt_log.cpp b/ydb/core/kqp/opt/logical/kqp_opt_log.cpp index 657252efaa36..1f81d71f5b29 100644 --- a/ydb/core/kqp/opt/logical/kqp_opt_log.cpp +++ b/ydb/core/kqp/opt/logical/kqp_opt_log.cpp @@ -22,11 +22,12 @@ using namespace NYql::NNodes; class TKqpLogicalOptTransformer : public TOptimizeTransformerBase { public: TKqpLogicalOptTransformer(TTypeAnnotationContext& typesCtx, const TIntrusivePtr& kqpCtx, - const TKikimrConfiguration::TPtr& config) + const TKikimrConfiguration::TPtr& config, TKqpProviderContext& pctx) : TOptimizeTransformerBase(nullptr, NYql::NLog::EComponent::ProviderKqp, {}) , TypesCtx(typesCtx) , KqpCtx(*kqpCtx) , Config(config) + , Pctx(pctx) { #define HNDL(name) "KqpLogical-"#name, Hndl(&TKqpLogicalOptTransformer::name) AddHandler(0, &TCoFlatMap::Match, HNDL(PushPredicateToReadTable)); @@ -135,9 +136,8 @@ class TKqpLogicalOptTransformer : public TOptimizeTransformerBase { TMaybeNode OptimizeEquiJoinWithCosts(TExprBase node, TExprContext& ctx) { auto maxDPccpDPTableSize = Config->MaxDPccpDPTableSize.Get().GetOrElse(TDqSettings::TDefault::MaxDPccpDPTableSize); - TKqpProviderContext providerContext(KqpCtx); TExprBase output = DqOptimizeEquiJoinWithCosts(node, ctx, TypesCtx, Config->CostBasedOptimizationLevel.Get().GetOrElse(TDqSettings::TDefault::CostBasedOptimizationLevel), - maxDPccpDPTableSize, providerContext, [](auto& rels, auto label, auto node, auto stat) { + maxDPccpDPTableSize, Pctx, [](auto& rels, auto label, auto node, auto stat) { rels.emplace_back(std::make_shared(TString(label), stat, node)); }); DumpAppliedRule("OptimizeEquiJoinWithCosts", node.Ptr(), output.Ptr(), ctx); @@ -274,12 +274,14 @@ class TKqpLogicalOptTransformer : public TOptimizeTransformerBase { TTypeAnnotationContext& TypesCtx; const TKqpOptimizeContext& KqpCtx; const TKikimrConfiguration::TPtr& Config; + TKqpProviderContext& Pctx; }; TAutoPtr CreateKqpLogOptTransformer(const TIntrusivePtr& kqpCtx, - TTypeAnnotationContext& typesCtx, const TKikimrConfiguration::TPtr& config) + TTypeAnnotationContext& typesCtx, const TKikimrConfiguration::TPtr& config, + TKqpProviderContext& pctx) { - return THolder(new TKqpLogicalOptTransformer(typesCtx, kqpCtx, config)); + return THolder(new TKqpLogicalOptTransformer(typesCtx, kqpCtx, config, pctx)); } } // namespace NKikimr::NKqp::NOpt diff --git a/ydb/core/kqp/opt/logical/kqp_opt_log.h b/ydb/core/kqp/opt/logical/kqp_opt_log.h index e833934a54be..3e5e9ed87996 100644 --- a/ydb/core/kqp/opt/logical/kqp_opt_log.h +++ b/ydb/core/kqp/opt/logical/kqp_opt_log.h @@ -1,12 +1,14 @@ #pragma once #include +#include namespace NKikimr::NKqp::NOpt { struct TKqpOptimizeContext; TAutoPtr CreateKqpLogOptTransformer(const TIntrusivePtr& kqpCtx, - NYql::TTypeAnnotationContext& typesCtx, const NYql::TKikimrConfiguration::TPtr& config); + NYql::TTypeAnnotationContext& typesCtx, const NYql::TKikimrConfiguration::TPtr& config, + NKikimr::NKqp::NOpt::TKqpProviderContext& pctx); } // namespace NKikimr::NKqp::NOpt diff --git a/ydb/library/yql/core/cbo/cbo_optimizer_new.cpp b/ydb/library/yql/core/cbo/cbo_optimizer_new.cpp index 2ee3d1a0d900..c7ce233d76f5 100644 --- a/ydb/library/yql/core/cbo/cbo_optimizer_new.cpp +++ b/ydb/library/yql/core/cbo/cbo_optimizer_new.cpp @@ -63,12 +63,13 @@ void TRelOptimizerNode::Print(std::stringstream& stream, int ntabs) { } TJoinOptimizerNode::TJoinOptimizerNode(const std::shared_ptr& left, const std::shared_ptr& right, - const std::set>& joinConditions, const EJoinKind joinType, bool nonReorderable) : + const std::set>& joinConditions, const EJoinKind joinType, const EJoinAlgoType joinAlgo, bool nonReorderable) : IBaseOptimizerNode(JoinNodeType), LeftArg(left), RightArg(right), JoinConditions(joinConditions), - JoinType(joinType) { + JoinType(joinType), + JoinAlgo(joinAlgo) { IsReorderable = (JoinType==EJoinKind::InnerJoin) && (nonReorderable==false); } diff --git a/ydb/library/yql/core/cbo/cbo_optimizer_new.h b/ydb/library/yql/core/cbo/cbo_optimizer_new.h index 629c92cf56bd..256252241e76 100644 --- a/ydb/library/yql/core/cbo/cbo_optimizer_new.h +++ b/ydb/library/yql/core/cbo/cbo_optimizer_new.h @@ -87,12 +87,12 @@ TString ConvertToJoinString(const EJoinKind kind); struct IProviderContext { virtual ~IProviderContext() = default; - virtual double ComputeJoinCost(const TOptimizerStatistics& leftStats, const TOptimizerStatistics& rightStats, EJoinImplType joinImpl) const = 0; + virtual double ComputeJoinCost(const TOptimizerStatistics& leftStats, const TOptimizerStatistics& rightStats, EJoinAlgoType joinAlgol) const = 0; virtual bool IsJoinApplicable(const std::shared_ptr& left, const std::shared_ptr& right, const std::set>& joinConditions, - EJoinImplType joinImpl) = 0; + EJoinAlgoType joinAlgo) = 0; }; @@ -100,23 +100,23 @@ struct IProviderContext { * Temporary solution for default provider context */ -struct TDummyProviderContext : IProviderContext { +struct TDummyProviderContext : public IProviderContext { TDummyProviderContext() {} - double ComputeJoinCost(const TOptimizerStatistics& leftStats, const TOptimizerStatistics& rightStats, EJoinImplType joinImpl) const { - Y_UNUSED(joinImpl); + double ComputeJoinCost(const TOptimizerStatistics& leftStats, const TOptimizerStatistics& rightStats, EJoinAlgoType joinAlgo) const override { + Y_UNUSED(joinAlgo); return leftStats.Nrows + 2.0 * rightStats.Nrows; } bool IsJoinApplicable(const std::shared_ptr& left, const std::shared_ptr& right, const std::set>& joinConditions, - EJoinImplType joinImpl) { + EJoinAlgoType joinAlgo) override { Y_UNUSED(left); Y_UNUSED(right); Y_UNUSED(joinConditions); - Y_UNUSED(joinImpl); + Y_UNUSED(joinAlgo); return true; } @@ -139,10 +139,11 @@ struct TJoinOptimizerNode : public IBaseOptimizerNode { std::shared_ptr RightArg; std::set> JoinConditions; EJoinKind JoinType; + EJoinAlgoType JoinAlgo; bool IsReorderable; TJoinOptimizerNode(const std::shared_ptr& left, const std::shared_ptr& right, - const std::set>& joinConditions, const EJoinKind joinType, bool nonReorderable=false); + const std::set>& joinConditions, const EJoinKind joinType, const EJoinAlgoType joinAlgo, bool nonReorderable=false); virtual ~TJoinOptimizerNode() {} virtual TVector Labels(); virtual void Print(std::stringstream& stream, int ntabs=0); diff --git a/ydb/library/yql/core/yql_cost_function.cpp b/ydb/library/yql/core/yql_cost_function.cpp index 23d7ea76aebf..dcf395ca408e 100644 --- a/ydb/library/yql/core/yql_cost_function.cpp +++ b/ydb/library/yql/core/yql_cost_function.cpp @@ -39,7 +39,7 @@ bool NDq::operator < (const NDq::TJoinColumn& c1, const NDq::TJoinColumn& c2) { */ TOptimizerStatistics NYql::ComputeJoinStats(const TOptimizerStatistics& leftStats, const TOptimizerStatistics& rightStats, - const TVector& leftJoinKeys, const TVector& rightJoinKeys, EJoinImplType joinImpl, const IProviderContext& ctx) { + const TVector& leftJoinKeys, const TVector& rightJoinKeys, EJoinAlgoType joinAlgo, const IProviderContext& ctx) { double newCard; EStatisticsType outputType; @@ -70,7 +70,7 @@ TOptimizerStatistics NYql::ComputeJoinStats(const TOptimizerStatistics& leftStat int newNCols = leftStats.Ncols + rightStats.Ncols; - double cost = ctx.ComputeJoinCost(leftStats, rightStats, joinImpl) + double cost = ctx.ComputeJoinCost(leftStats, rightStats, joinAlgo) + newCard + leftStats.Cost + rightStats.Cost; @@ -78,7 +78,7 @@ TOptimizerStatistics NYql::ComputeJoinStats(const TOptimizerStatistics& leftStat } TOptimizerStatistics NYql::ComputeJoinStats(const TOptimizerStatistics& leftStats, const TOptimizerStatistics& rightStats, - const std::set>& joinConditions, EJoinImplType joinImpl, const IProviderContext& ctx) { + const std::set>& joinConditions, EJoinAlgoType joinAlgo, const IProviderContext& ctx) { TVector leftJoinKeys; TVector rightJoinKeys; @@ -88,5 +88,5 @@ TOptimizerStatistics NYql::ComputeJoinStats(const TOptimizerStatistics& leftStat rightJoinKeys.emplace_back(c.second.AttributeName); } - return ComputeJoinStats(leftStats, rightStats, leftJoinKeys, rightJoinKeys, joinImpl, ctx); + return ComputeJoinStats(leftStats, rightStats, leftJoinKeys, rightJoinKeys, joinAlgo, ctx); } diff --git a/ydb/library/yql/core/yql_cost_function.h b/ydb/library/yql/core/yql_cost_function.h index 7d481fcc9ced..9a040404cfce 100644 --- a/ydb/library/yql/core/yql_cost_function.h +++ b/ydb/library/yql/core/yql_cost_function.h @@ -45,19 +45,19 @@ bool operator < (const TJoinColumn& c1, const TJoinColumn& c2); } -enum EJoinImplType { +enum EJoinAlgoType { DictJoin, MapJoin, GraceJoin, LookupJoin }; -static const EJoinImplType AllJoinTypes[] = { DictJoin, MapJoin, GraceJoin, LookupJoin }; +static const EJoinAlgoType AllJoinTypes[] = { DictJoin, MapJoin, GraceJoin, LookupJoin }; TOptimizerStatistics ComputeJoinStats(const TOptimizerStatistics& leftStats, const TOptimizerStatistics& rightStats, - const std::set>& joinConditions, EJoinImplType joinType, const IProviderContext& ctx); + const std::set>& joinConditions, EJoinAlgoType joinAlgo, const IProviderContext& ctx); TOptimizerStatistics ComputeJoinStats(const TOptimizerStatistics& leftStats, const TOptimizerStatistics& rightStats, - const TVector& leftJoinKeys, const TVector& rightJoinKeys, EJoinImplType joinType, const IProviderContext& ctx); + const TVector& leftJoinKeys, const TVector& rightJoinKeys, EJoinAlgoType joinAlgo, const IProviderContext& ctx); } \ No newline at end of file diff --git a/ydb/library/yql/dq/opt/dq_opt_join_cost_based.cpp b/ydb/library/yql/dq/opt/dq_opt_join_cost_based.cpp index af637ee37877..92d17378cc31 100644 --- a/ydb/library/yql/dq/opt/dq_opt_join_cost_based.cpp +++ b/ydb/library/yql/dq/opt/dq_opt_join_cost_based.cpp @@ -97,14 +97,18 @@ void ComputeJoinConditions(const TCoEquiJoinTuple& joinTuple, std::shared_ptr MakeJoin(std::shared_ptr left, std::shared_ptr right, const std::set>& joinConditions, - EJoinImplType joinImpl, + EJoinAlgoType joinAlgo, IProviderContext& ctx) { - auto res = std::make_shared(left, right, joinConditions, EJoinKind::InnerJoin); - res->Stats = std::make_shared( ComputeJoinStats(*left->Stats, *right->Stats, joinConditions, joinImpl, ctx)); + auto res = std::make_shared(left, right, joinConditions, EJoinKind::InnerJoin, joinAlgo); + res->Stats = std::make_shared( ComputeJoinStats(*left->Stats, *right->Stats, joinConditions, joinAlgo, ctx)); return res; } +/** + * Iterate over all join algorithms and pick the best join that is applicable. + * Also considers commuting joins +*/ std::shared_ptr PickBestJoin(std::shared_ptr left, std::shared_ptr right, const std::set>& leftJoinConditions, @@ -145,6 +149,9 @@ std::shared_ptr PickBestJoin(std::shared_ptr PickBestNonReorderabeJoin(std::shared_ptr left, std::shared_ptr right, const std::set>& leftJoinConditions, @@ -927,7 +934,7 @@ std::shared_ptr ConvertToJoinTree(const TCoEquiJoinTuple& jo TJoinColumn(rightScope, rightColumn))); } - return std::make_shared(left,right,joinConds,ConvertToJoinKind(joinTuple.Type().StringValue())); + return std::make_shared(left, right, joinConds, ConvertToJoinKind(joinTuple.Type().StringValue()), EJoinAlgoType::DictJoin); } /** @@ -992,7 +999,7 @@ void ComputeStatistics(const std::shared_ptr& join, IProvide if (join->RightArg->Kind == EOptimizerNodeKind::JoinNodeType) { ComputeStatistics(static_pointer_cast(join->RightArg), ctx); } - join->Stats = std::make_shared(ComputeJoinStats(*join->LeftArg->Stats, *join->RightArg->Stats, join->JoinConditions, EJoinImplType::DictJoin, ctx)); + join->Stats = std::make_shared(ComputeJoinStats(*join->LeftArg->Stats, *join->RightArg->Stats, join->JoinConditions, EJoinAlgoType::DictJoin, ctx)); } /** @@ -1086,7 +1093,7 @@ class TOptimizerNativeNew: public IOptimizerNew { if (join->RightArg->Kind == EOptimizerNodeKind::JoinNodeType) { join->RightArg = OptimizeSubtree(static_pointer_cast(join->RightArg), MaxDPccpDPTableSize, Pctx); } - join->Stats = std::make_shared(ComputeJoinStats(*join->LeftArg->Stats, *join->RightArg->Stats, join->JoinConditions, EJoinImplType::DictJoin, Pctx)); + join->Stats = std::make_shared(ComputeJoinStats(*join->LeftArg->Stats, *join->RightArg->Stats, join->JoinConditions, EJoinAlgoType::DictJoin, Pctx)); } // Optimize the root