From 0434074e45a9978d1429b06021cefbbac64a24ef Mon Sep 17 00:00:00 2001 From: yangke1125 <2987765698@qq.com> Date: Wed, 4 Oct 2023 20:29:47 +0800 Subject: [PATCH] Enter --- src/gausskernel/optimizer/path/costsize.cpp | 293 ++++++++++---------- 1 file changed, 150 insertions(+), 143 deletions(-) diff --git a/src/gausskernel/optimizer/path/costsize.cpp b/src/gausskernel/optimizer/path/costsize.cpp index 56085451c..abe457151 100644 --- a/src/gausskernel/optimizer/path/costsize.cpp +++ b/src/gausskernel/optimizer/path/costsize.cpp @@ -4828,25 +4828,40 @@ Cost cost_rescan_material(double rows, int width, OpMemInfo* mem_info, bool vect * the run_cost charge in cost_sort, and also see comments in * cost_material before you change it.) */ - double local_rows = rows / dop; - Cost run_cost = u_sess->attr.attr_sql.cpu_operator_cost * local_rows; - double nbytes = relation_byte_size(local_rows, width, vectorized, true, false); - long work_mem_bytes = u_sess->opt_cxt.op_work_mem * 1024L / dop; - /* It will spill, so account for re-read cost */ - double npages = ceil(nbytes / BLCKSZ); - double disk_cost = u_sess->attr.attr_sql.seq_page_cost * npages; + // 计算每个执行节点的本地行数 +double local_rows = rows / dop; - if (nbytes > work_mem_bytes) { - run_cost += disk_cost; - } - if (mem_info != NULL) { - mem_info->opMem = u_sess->opt_cxt.op_work_mem; - mem_info->maxMem = nbytes / 1024L * dop; - mem_info->minMem = mem_info->maxMem / SORT_MAX_DISK_SIZE; - mem_info->regressCost = disk_cost; - } +// 计算运行成本,考虑每个执行节点的本地行数和 CPU 运算成本 +Cost run_cost = u_sess->attr.attr_sql.cpu_operator_cost * local_rows; + +// 计算每个执行节点需要的字节数,考虑本地行数、宽度、是否矢量化等因素 +double nbytes = relation_byte_size(local_rows, width, vectorized, true, false); + +// 计算每个执行节点的工作内存字节数,根据工作内存配置和并行度进行分配 +long work_mem_bytes = u_sess->opt_cxt.op_work_mem * 1024L / dop; + +// 计算数据需要的页数,以 BLCKSZ 为单位 +double npages = ceil(nbytes / BLCKSZ); + +// 计算磁盘成本,考虑数据页数和磁盘顺序扫描成本 +double disk_cost = u_sess->attr.attr_sql.seq_page_cost * npages; + +// 如果数据字节数超过工作内存限制,考虑磁盘读取成本 +if (nbytes > work_mem_bytes) { + run_cost += disk_cost; +} + +// 如果传入了内存信息结构体,则更新内存信息 +if (mem_info != NULL) { + mem_info->opMem = u_sess->opt_cxt.op_work_mem; + mem_info->maxMem = nbytes / 1024L * dop; + mem_info->minMem = mem_info->maxMem / SORT_MAX_DISK_SIZE; + mem_info->regressCost = disk_cost; +} + +// 返回运行成本 +return run_cost; - return run_cost; } #ifdef PGXC @@ -4921,42 +4936,44 @@ static bool cost_qual_eval_walker(Node* node, cost_qual_eval_context* context) * cost more than once. If the clause's cost hasn't been computed yet, * the field's startup value will contain -1. */ - if (IsA(node, RestrictInfo)) { - RestrictInfo* rinfo = (RestrictInfo*)node; + // 检查传入的节点是否为 RestrictInfo 类型 +if (IsA(node, RestrictInfo)) { + RestrictInfo* rinfo = (RestrictInfo*)node; - if (rinfo->eval_cost.startup < 0) { - cost_qual_eval_context locContext; + // 如果评估成本中的 startup 值小于 0,则需要计算 + if (rinfo->eval_cost.startup < 0) { + cost_qual_eval_context locContext; - locContext.root = context->root; - locContext.total.startup = 0; + // 初始化评估上下文,设置根节点和成本初始值 + locContext.root = context->root; + locContext.total.startup = 0; + locContext.total.per_tuple = 0; + + // 如果存在 OR 子句,计算 OR 子句中的表达式成本 + if (rinfo->orclause) + (void)cost_qual_eval_walker((Node*)rinfo->orclause, &locContext); + else + (void)cost_qual_eval_walker((Node*)rinfo->clause, &locContext); + + // 如果表达式被标记为伪常量,将 startup 成本添加到总成本中 + if (rinfo->pseudoconstant) { + locContext.total.startup += locContext.total.per_tuple; locContext.total.per_tuple = 0; - - /* - * For an OR clause, recurse into the marked-up tree so that we - * set the eval_cost for contained RestrictInfos too. - */ - if (rinfo->orclause) - (void)cost_qual_eval_walker((Node*)rinfo->orclause, &locContext); - else - (void)cost_qual_eval_walker((Node*)rinfo->clause, &locContext); - - /* - * If the RestrictInfo is marked pseudoconstant, it will be tested - * only once, so treat its cost as all startup cost. - */ - if (rinfo->pseudoconstant) { - /* count one execution during startup */ - locContext.total.startup += locContext.total.per_tuple; - locContext.total.per_tuple = 0; - } - rinfo->eval_cost = locContext.total; } - context->total.startup += rinfo->eval_cost.startup; - context->total.per_tuple += rinfo->eval_cost.per_tuple; - /* do NOT recurse into children */ - return false; + + // 将计算得到的成本赋值给 RestrictInfo 结构体 + rinfo->eval_cost = locContext.total; } + // 将 RestrictInfo 的评估成本合并到上下文中的总成本中 + context->total.startup += rinfo->eval_cost.startup; + context->total.per_tuple += rinfo->eval_cost.per_tuple; + + // 返回 false 表示不需要继续递归处理子节点 + return false; +} + + /* * For each operator or function node in the given tree, we charge the * estimated execution cost given by pg_proc.procost (remember to multiply @@ -4979,103 +4996,80 @@ static bool cost_qual_eval_walker(Node* node, cost_qual_eval_context* context) * moreover, since our rowcount estimates for functions tend to be pretty * phony, the results would also be pretty phony. */ - if (IsA(node, FuncExpr)) { - context->total.per_tuple += get_func_cost(((FuncExpr*)node)->funcid) * u_sess->attr.attr_sql.cpu_operator_cost; - } else if (IsA(node, OpExpr) || IsA(node, DistinctExpr) || IsA(node, NullIfExpr)) { - /* rely on struct equivalence to treat these all alike */ - set_opfuncid((OpExpr*)node); - context->total.per_tuple += get_func_cost(((OpExpr*)node)->opfuncid) * u_sess->attr.attr_sql.cpu_operator_cost; - } else if (IsA(node, ScalarArrayOpExpr)) { - /* - * Estimate that the operator will be applied to about half of the - * array elements before the answer is determined. - */ - ScalarArrayOpExpr* saop = (ScalarArrayOpExpr*)node; - Node* arraynode = (Node*)lsecond(saop->args); + // 检查传入的节点是否为 FuncExpr 类型 +if (IsA(node, FuncExpr)) { + // 如果是 FuncExpr 类型,将函数的评估成本添加到总成本中 + context->total.per_tuple += get_func_cost(((FuncExpr*)node)->funcid) * u_sess->attr.attr_sql.cpu_operator_cost; +} else if (IsA(node, OpExpr) || IsA(node, DistinctExpr) || IsA(node, NullIfExpr)) { + // 如果是 OpExpr、DistinctExpr 或 NullIfExpr 类型,设置操作符的函数 ID 并添加其评估成本到总成本中 + set_opfuncid((OpExpr*)node); + context->total.per_tuple += get_func_cost(((OpExpr*)node)->opfuncid) * u_sess->attr.attr_sql.cpu_operator_cost; +} else if (IsA(node, ScalarArrayOpExpr)) { + // 如果是 ScalarArrayOpExpr 类型,设置标量数组操作符的函数 ID 并添加其评估成本到总成本中 + ScalarArrayOpExpr* saop = (ScalarArrayOpExpr*)node; + Node* arraynode = (Node*)lsecond(saop->args); - set_sa_opfuncid(saop); - context->total.per_tuple += get_func_cost(saop->opfuncid) * u_sess->attr.attr_sql.cpu_operator_cost * - estimate_array_length(arraynode) * 0.5; - } else if (IsA(node, Aggref) || IsA(node, WindowFunc)) { - /* - * Aggref and WindowFunc nodes are (and should be) treated like Vars, - * ie, zero execution cost in the current model, because they behave - * essentially like Vars in execQual.c. We disregard the costs of - * their input expressions for the same reason. The actual execution - * costs of the aggregate/window functions and their arguments have to - * be factored into plan-node-specific costing of the Agg or WindowAgg - * plan node. - */ - return false; /* don't recurse into children */ - } else if (IsA(node, CoerceViaIO)) { - CoerceViaIO* iocoerce = (CoerceViaIO*)node; - Oid iofunc; - Oid typioparam; - bool typisvarlena = false; + set_sa_opfuncid(saop); + context->total.per_tuple += get_func_cost(saop->opfuncid) * u_sess->attr.attr_sql.cpu_operator_cost * + estimate_array_length(arraynode) * 0.5; +} else if (IsA(node, Aggref) || IsA(node, WindowFunc)) { + // 如果是 Aggref 或 WindowFunc 类型,返回 false 表示不需要继续处理子节点 + return false; +} else if (IsA(node, CoerceViaIO)) { + // 如果是 CoerceViaIO 类型,计算输入和输出函数的评估成本并添加到总成本中 + CoerceViaIO* iocoerce = (CoerceViaIO*)node; + Oid iofunc; + Oid typioparam; + bool typisvarlena = false; - /* check the result type's input function */ - getTypeInputInfo(iocoerce->resulttype, &iofunc, &typioparam); - context->total.per_tuple += get_func_cost(iofunc) * u_sess->attr.attr_sql.cpu_operator_cost; - /* check the input type's output function */ - getTypeOutputInfo(exprType((Node*)iocoerce->arg), &iofunc, &typisvarlena); - context->total.per_tuple += get_func_cost(iofunc) * u_sess->attr.attr_sql.cpu_operator_cost; - } else if (IsA(node, ArrayCoerceExpr)) { - ArrayCoerceExpr* acoerce = (ArrayCoerceExpr*)node; - Node* arraynode = (Node*)acoerce->arg; + getTypeInputInfo(iocoerce->resulttype, &iofunc, &typioparam); + context->total.per_tuple += get_func_cost(iofunc) * u_sess->attr.attr_sql.cpu_operator_cost; - if (OidIsValid(acoerce->elemfuncid)) - context->total.per_tuple += get_func_cost(acoerce->elemfuncid) * u_sess->attr.attr_sql.cpu_operator_cost * - estimate_array_length(arraynode); - } else if (IsA(node, RowCompareExpr)) { - /* Conservatively assume we will check all the columns */ - RowCompareExpr* rcexpr = (RowCompareExpr*)node; - ListCell* lc = NULL; + getTypeOutputInfo(exprType((Node*)iocoerce->arg), &iofunc, &typisvarlena); + context->total.per_tuple += get_func_cost(iofunc) * u_sess->attr.attr_sql.cpu_operator_cost; +} else if (IsA(node, ArrayCoerceExpr)) { + // 如果是 ArrayCoerceExpr 类型,根据元素函数 ID 计算评估成本并添加到总成本中 + ArrayCoerceExpr* acoerce = (ArrayCoerceExpr*)node; + Node* arraynode = (Node*)acoerce->arg; - foreach (lc, rcexpr->opnos) { - Oid opid = lfirst_oid(lc); + if (OidIsValid(acoerce->elemfuncid)) + context->total.per_tuple += get_func_cost(acoerce->elemfuncid) * u_sess->attr.attr_sql.cpu_operator_cost * + estimate_array_length(arraynode); +} else if (IsA(node, RowCompareExpr)) { + // 如果是 RowCompareExpr 类型,计算操作符函数的评估成本并添加到总成本中 + RowCompareExpr* rcexpr = (RowCompareExpr*)node; + ListCell* lc = NULL; - context->total.per_tuple += get_func_cost(get_opcode(opid)) * u_sess->attr.attr_sql.cpu_operator_cost; - } - } else if (IsA(node, CurrentOfExpr)) { - /* Report high cost to prevent selection of anything but TID scan */ - context->total.startup += g_instance.cost_cxt.disable_cost; - } else if (IsA(node, SubLink)) { - /* This routine should not be applied to un-planned expressions */ - ereport(ERROR, - (errmodule(MOD_OPT), - errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), - errmsg("cannot handle unplanned sub-select when costing quals"))); - } else if (IsA(node, SubPlan)) { - /* - * A subplan node in an expression typically indicates that the - * subplan will be executed on each evaluation, so charge accordingly. - * (Sub-selects that can be executed as InitPlans have already been - * removed from the expression.) - */ - SubPlan* subplan = (SubPlan*)node; + foreach (lc, rcexpr->opnos) { + Oid opid = lfirst_oid(lc); - context->total.startup += subplan->startup_cost; - context->total.per_tuple += subplan->per_call_cost; - - /* - * We don't want to recurse into the testexpr, because it was already - * counted in the SubPlan node's costs. So we're done. - */ - return false; - } else if (IsA(node, AlternativeSubPlan)) { - /* - * Arbitrarily use the first alternative plan for costing. (We should - * certainly only include one alternative, and we don't yet have - * enough information to know which one the executor is most likely to - * use.) - */ - AlternativeSubPlan* asplan = (AlternativeSubPlan*)node; - - return cost_qual_eval_walker((Node*)linitial(asplan->subplans), context); + context->total.per_tuple += get_func_cost(get_opcode(opid)) * u_sess->attr.attr_sql.cpu_operator_cost; } +} else if (IsA(node, CurrentOfExpr)) { + // 如果是 CurrentOfExpr 类型,添加禁用成本到启动成本中 + context->total.startup += g_instance.cost_cxt.disable_cost; +} else if (IsA(node, SubLink)) { + // 如果是 SubLink 类型,报错,因为无法处理未计划的子查询 + ereport(ERROR, + (errmodule(MOD_OPT), + errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), + errmsg("cannot handle unplanned sub-select when costing quals"))); +} else if (IsA(node, SubPlan)) { + // 如果是 SubPlan 类型,将子查询的启动成本和每次调用成本添加到总成本中 + SubPlan* subplan = (SubPlan*)node; - /* recurse into children */ - return expression_tree_walker(node, (bool (*)())cost_qual_eval_walker, (void*)context); + context->total.startup += subplan->startup_cost; + context->total.per_tuple += subplan->per_call_cost; + + // 返回 false 表示不需要继续递归处理子节点 + return false; +} else if (IsA(node, AlternativeSubPlan)) { + // 如果是 AlternativeSubPlan 类型,继续处理第一个替代子计划节点 + return cost_qual_eval_walker((Node*)linitial(asplan->subplans), context); +} + +// 继续递归处理节点的子节点 +return expression_tree_walker(node, (bool (*)())cost_qual_eval_walker, (void*)context); } /* @@ -5090,18 +5084,23 @@ static bool cost_qual_eval_walker(Node* node, cost_qual_eval_context* context) * some of the quals. We assume baserestrictcost was previously set * by set_baserel_size_estimates(). */ +// 这是一个名为 get_restriction_qual_cost 的静态函数,计算限制条件的成本 static void get_restriction_qual_cost( PlannerInfo* root, RelOptInfo* baserel, ParamPathInfo* param_info, QualCost* qpqual_cost) { + // 如果 param_info 不为空,则计算 param_info 中的限制条件成本 if (param_info != NULL) { - /* Include costs of pushed-down clauses */ + // 调用 cost_qual_eval 函数计算限制条件的成本,并存储在 qpqual_cost 中 cost_qual_eval(qpqual_cost, param_info->ppi_clauses, root); + // 将基本关系的限制条件成本加到 qpqual_cost 中 qpqual_cost->startup += baserel->baserestrictcost.startup; qpqual_cost->per_tuple += baserel->baserestrictcost.per_tuple; - } else + } else { + // 如果 param_info 为空,则直接将基本关系的限制条件成本赋值给 qpqual_cost *qpqual_cost = baserel->baserestrictcost; -} + } + /* * compute_semi_anti_join_factors @@ -5222,31 +5221,39 @@ void compute_semi_anti_join_factors(PlannerInfo* root, RelOptInfo* outerrel, Rel * unmatched outer tuple is cheap to process, whereas otherwise it's probably * expensive. */ +// 这是一个名为 has_indexed_join_quals 的函数,检查是否有索引关联的连接条件 bool has_indexed_join_quals(NestPath* joinpath) { + // 获取连接路径的关联关系 ID 集合 Relids joinrelids = joinpath->path.parent->relids; + // 获取连接路径的内部路径 Path* innerpath = joinpath->innerjoinpath; + // 用于存储索引条件的列表 List* indexclauses = NIL; + // 标志,表示是否找到至少一个索引条件 bool found_one = false; + // 用于遍历列表的迭代器 ListCell* lc = NULL; - /* If join still has quals to evaluate, it's not fast */ + // 如果连接路径有连接限制条件,则返回 false if (joinpath->joinrestrictinfo != NIL) return false; - /* Nor if the inner path isn't parameterized at all */ + + // 如果内部路径没有 param_info,则返回 false if (innerpath->param_info == NULL) return false; - /* Find the indexclauses list for the inner scan */ + // 根据内部路径的类型,获取相应的索引条件列表 switch (innerpath->pathtype) { case T_IndexScan: case T_IndexOnlyScan: indexclauses = ((IndexPath*)innerpath)->indexclauses; break; case T_BitmapHeapScan: { - /* Accept only a simple bitmap scan, not AND/OR cases */ + // 如果内部路径是 BitmapHeapScan,则获取其 bitmapqual Path* bmqual = ((BitmapHeapPath*)innerpath)->bitmapqual; + // 如果 bitmapqual 是 IndexPath,则获取其索引条件列表 if (IsA(bmqual, IndexPath)) indexclauses = ((IndexPath*)bmqual)->indexclauses; else