diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 1831ea81cf..6a64282027 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -24,6 +24,7 @@ #include "nodes/extensible.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" +#include "optimizer/cost.h" #include "parser/parsetree.h" #include "rewrite/rewriteHandler.h" #include "storage/bufmgr.h" @@ -1441,7 +1442,8 @@ ExplainNode(PlanState *planstate, List *ancestors, { if (es->timing) appendStringInfo(es->str, - " (actual time=%.3f..%.3f rows=%.0f loops=%.0f)", + " (actual cost=%.2f..%.2f time=%.3f..%.3f rows=%.0f loops=%.0f)", + plan->cost_info->startup_cost, actual_total_cost(plan, rows), startup_ms, total_ms, rows, nloops); else appendStringInfo(es->str, diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 4b9be13f08..16c2c904bb 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -201,32 +201,23 @@ clamp_row_est(double nrows) /* - * cost_seqscan - * Determines and returns the cost of scanning a relation sequentially. - * - * 'baserel' is the relation to be scanned - * 'param_info' is the ParamPathInfo if this is a parameterized path, else NULL + * Fill the cost info structure. Later we use this structure to compute the + * cost estimation. */ -void -cost_seqscan(Path *path, PlannerInfo *root, +CostInfo * +cost_info_seqscan(Path *path, PlannerInfo *root, RelOptInfo *baserel, ParamPathInfo *param_info) { - Cost startup_cost = 0; - Cost cpu_run_cost; - Cost disk_run_cost; - double spc_seq_page_cost; + Cost per_page_cost; + Cost per_row_cost; + Cost per_tuple_cost; + Cost startup_cost = 0.0; + double npages; + double ntuples; + double parallel_divisor; QualCost qpqual_cost; - Cost cpu_per_tuple; + CostInfo *cost_info; - /* Should only be applied to base relations */ - Assert(baserel->relid > 0); - Assert(baserel->rtekind == RTE_RELATION); - - /* Mark the path with the correct row estimate */ - if (param_info) - path->rows = param_info->ppi_rows; - else - path->rows = baserel->rows; if (!enable_seqscan) startup_cost += disable_cost; @@ -234,31 +225,93 @@ cost_seqscan(Path *path, PlannerInfo *root, /* fetch estimated page cost for tablespace containing table */ get_tablespace_page_costs(baserel->reltablespace, NULL, - &spc_seq_page_cost); + &per_page_cost); - /* - * disk costs - */ - disk_run_cost = spc_seq_page_cost * baserel->pages; + npages = baserel->pages; /* CPU costs */ get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost); - startup_cost += qpqual_cost.startup; - cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple; - cpu_run_cost = cpu_per_tuple * baserel->tuples; + startup_cost = qpqual_cost.startup; + per_tuple_cost = cpu_tuple_cost + qpqual_cost.per_tuple; + ntuples = baserel->tuples; /* tlist eval costs are paid per output row, not per tuple scanned */ startup_cost += path->pathtarget->cost.startup; - cpu_run_cost += path->pathtarget->cost.per_tuple * path->rows; + per_row_cost = path->pathtarget->cost.per_tuple; + parallel_divisor = 1.0; /* Adjust costing for parallelism, if used. */ if (path->parallel_workers > 0) { - double parallel_divisor = get_parallel_divisor(path); + parallel_divisor = get_parallel_divisor(path); + } - /* The CPU cost is divided among all the workers. */ - cpu_run_cost /= parallel_divisor; + cost_info = palloc(sizeof(CostInfo)); + cost_info->per_page_cost = per_page_cost; + cost_info->per_row_cost = per_row_cost; + cost_info->per_tuple_cost = per_tuple_cost; + cost_info->startup_cost = startup_cost; + cost_info->npages = npages; + cost_info->ntuples = ntuples; + cost_info->parallel_divisor= parallel_divisor; + + return cost_info; +} + + +/* + * Calculate the total cost of a SeqScan + */ +double +total_cost_seqscan(CostInfo *cost_info, double nrows) +{ + Cost disk_run_cost; + Cost cpu_run_cost; + + /* + * disk costs + */ + disk_run_cost = cost_info->per_page_cost * cost_info->npages; + cpu_run_cost = cost_info->per_tuple_cost * cost_info->ntuples + \ + cost_info->per_row_cost * nrows; + + /* The CPU cost is divided among all the workers. */ + cpu_run_cost /= cost_info->parallel_divisor; + + return cost_info->startup_cost + cpu_run_cost + disk_run_cost; +} + + +/* + * cost_seqscan + * Determines and returns the cost of scanning a relation sequentially. + * + * 'baserel' is the relation to be scanned + * 'param_info' is the ParamPathInfo if this is a parameterized path, else NULL + */ +void +cost_seqscan(Path *path, PlannerInfo *root, + RelOptInfo *baserel, ParamPathInfo *param_info) +{ + double nrows; + CostInfo *cost_info; + + /* Should only be applied to base relations */ + Assert(baserel->relid > 0); + Assert(baserel->rtekind == RTE_RELATION); + + /* Mark the path with the correct row estimate */ + if (param_info) + nrows = param_info->ppi_rows; + else + nrows = baserel->rows; + + cost_info = cost_info_seqscan(path, root, baserel, param_info); + + /* Adjust costing for parallelism, if used. */ + if (path->parallel_workers > 0) + { /* * It may be possible to amortize some of the I/O cost, but probably * not very much, because most operating systems already do aggressive @@ -270,11 +323,13 @@ cost_seqscan(Path *path, PlannerInfo *root, * In the case of a parallel plan, the row count needs to represent * the number of tuples processed per worker. */ - path->rows = clamp_row_est(path->rows / parallel_divisor); + path->rows = clamp_row_est(path->rows / cost_info->parallel_divisor); } - path->startup_cost = startup_cost; - path->total_cost = startup_cost + cpu_run_cost + disk_run_cost; + path->startup_cost = cost_info->startup_cost; + path->total_cost = total_cost_seqscan(cost_info, nrows); + + pfree(cost_info); } /* @@ -5551,3 +5606,19 @@ compute_bitmap_pages(PlannerInfo *root, RelOptInfo *baserel, Path *bitmapqual, return pages_fetched; } + +/* + * Get the actual total cost by using the actual number of rows + */ +double +actual_total_cost(Plan *plan, double nrows) +{ + switch (nodeTag(plan)) + { + case T_SeqScan: + return total_cost_seqscan(plan->cost_info, nrows); + default: + break; + } + return -1.0; +} diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 236f506cfb..3133eb79c0 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -349,6 +349,8 @@ create_plan(PlannerInfo *root, Path *best_path) * re-used later */ root->plan_params = NIL; + plan->cost_info = cost_info_seqscan(best_path, root, + best_path->parent, best_path->param_info); return plan; } diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 6d087c268f..36534c619f 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -127,6 +127,8 @@ typedef struct Plan double plan_rows; /* number of rows plan is expected to emit */ int plan_width; /* average row width in bytes */ + struct CostInfo *cost_info; + /* * information needed for parallel query */ diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h index ac6de0f6be..4576d3747d 100644 --- a/src/include/optimizer/cost.h +++ b/src/include/optimizer/cost.h @@ -38,6 +38,17 @@ typedef enum CONSTRAINT_EXCLUSION_PARTITION /* apply c_e to otherrels only */ } ConstraintExclusionType; +typedef struct CostInfo +{ + Cost per_page_cost; + Cost per_row_cost; + Cost per_tuple_cost; + Cost startup_cost; + double npages; + double ntuples; + double parallel_divisor; +} CostInfo; + /* * prototypes for costsize.c @@ -199,4 +210,14 @@ extern PathTarget *set_pathtarget_cost_width(PlannerInfo *root, PathTarget *targ extern double compute_bitmap_pages(PlannerInfo *root, RelOptInfo *baserel, Path *bitmapqual, int loop_count, Cost *cost, double *tuple); + +extern CostInfo *cost_info_seqscan(Path *path, PlannerInfo *root, + RelOptInfo *baserel, ParamPathInfo *param_info); + +extern double total_cost_seqscan(CostInfo *cost_info, double nrows); + +/* The actual total cost calculated based on the instument. + * Currently, only the number of rows is used to show a proof of concept. + */ +extern double actual_total_cost(Plan *plan, double nrows); #endif /* COST_H */