From cc415a56d09a8da7c919088036b6097b70f10791 Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Thu, 22 Mar 2018 11:45:07 -0700 Subject: [PATCH] Basic planner and executor integration for JIT. This adds simple cost based plan time decision about whether JIT should be performed. jit_above_cost, jit_optimize_above_cost are compared with the total cost of a plan, and if the cost is above them JIT is performed / optimization is performed respectively. For that PlannedStmt and EState have a jitFlags (es_jit_flags) field that stores information about what JIT operations should be performed. EState now also has a new es_jit field, which can store a JitContext. When there are no errors the context is released in standard_ExecutorEnd(). It is likely that the default values for jit_[optimize_]above_cost will need to be adapted further, but in my test these values seem to work reasonably. Author: Andres Freund, with feedback by Peter Eisentraut Discussion: https://postgr.es/m/20170901064131.tazjxwus3k2w3ybh@alap3.anarazel.de --- src/backend/executor/execMain.c | 8 ++++++++ src/backend/executor/execParallel.c | 3 +++ src/backend/executor/execUtils.c | 3 +++ src/backend/jit/jit.c | 2 ++ src/backend/nodes/copyfuncs.c | 1 + src/backend/nodes/outfuncs.c | 1 + src/backend/nodes/readfuncs.c | 1 + src/backend/optimizer/plan/planner.c | 15 ++++++++++++++ src/backend/utils/misc/guc.c | 20 +++++++++++++++++++ src/backend/utils/misc/postgresql.conf.sample | 6 ++++++ src/include/jit/jit.h | 2 ++ src/include/nodes/execnodes.h | 8 ++++++++ src/include/nodes/plannodes.h | 2 ++ 13 files changed, 72 insertions(+) diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 91ba939bdc..890067757c 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -48,6 +48,7 @@ #include "commands/trigger.h" #include "executor/execdebug.h" #include "foreign/fdwapi.h" +#include "jit/jit.h" #include "mb/pg_wchar.h" #include "miscadmin.h" #include "optimizer/clauses.h" @@ -249,6 +250,9 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags) estate->es_top_eflags = eflags; estate->es_instrument = queryDesc->instrument_options; + if (queryDesc->plannedstmt) + estate->es_jit_flags = queryDesc->plannedstmt->jitFlags; + /* * Set up an AFTER-trigger statement context, unless told not to, or * unless it's EXPLAIN-only mode (when ExecutorFinish won't be called). @@ -496,6 +500,10 @@ standard_ExecutorEnd(QueryDesc *queryDesc) UnregisterSnapshot(estate->es_snapshot); UnregisterSnapshot(estate->es_crosscheck_snapshot); + /* release JIT context, if allocated */ + if (estate->es_jit) + jit_release_context(estate->es_jit); + /* * Must switch out of context before destroying it */ diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index 14b0b89463..52f1a96db5 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -73,6 +73,7 @@ typedef struct FixedParallelExecutorState int64 tuples_needed; /* tuple bound, see ExecSetTupleBound */ dsa_pointer param_exec; int eflags; + int jit_flags; } FixedParallelExecutorState; /* @@ -680,6 +681,7 @@ ExecInitParallelPlan(PlanState *planstate, EState *estate, fpes->tuples_needed = tuples_needed; fpes->param_exec = InvalidDsaPointer; fpes->eflags = estate->es_top_eflags; + fpes->jit_flags = estate->es_jit_flags; shm_toc_insert(pcxt->toc, PARALLEL_KEY_EXECUTOR_FIXED, fpes); /* Store query string */ @@ -1287,6 +1289,7 @@ ParallelQueryMain(dsm_segment *seg, shm_toc *toc) area = dsa_attach_in_place(area_space, seg); /* Start up the executor */ + queryDesc->plannedstmt->jitFlags = fpes->jit_flags; ExecutorStart(queryDesc, fpes->eflags); /* Special executor initialization steps for parallel workers */ diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index a8ae37ebc8..14b07b5d44 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -158,6 +158,9 @@ CreateExecutorState(void) estate->es_use_parallel_mode = false; + estate->es_jit_flags = 0; + estate->es_jit = NULL; + /* * Return the executor state structure */ diff --git a/src/backend/jit/jit.c b/src/backend/jit/jit.c index c17df1c985..1d74ec8c91 100644 --- a/src/backend/jit/jit.c +++ b/src/backend/jit/jit.c @@ -36,6 +36,8 @@ char *jit_provider = "llvmjit"; bool jit_debugging_support = false; bool jit_dump_bitcode = false; bool jit_profiling_support = false; +double jit_above_cost = 100000; +double jit_optimize_above_cost = 500000; static JitProviderCallbacks provider; static bool provider_successfully_loaded = false; diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 3ad4da64aa..c7293a60d7 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -87,6 +87,7 @@ _copyPlannedStmt(const PlannedStmt *from) COPY_SCALAR_FIELD(transientPlan); COPY_SCALAR_FIELD(dependsOnRole); COPY_SCALAR_FIELD(parallelModeNeeded); + COPY_SCALAR_FIELD(jitFlags); COPY_NODE_FIELD(planTree); COPY_NODE_FIELD(rtable); COPY_NODE_FIELD(resultRelations); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index fd80891954..f61ae03ac5 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -272,6 +272,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node) WRITE_BOOL_FIELD(transientPlan); WRITE_BOOL_FIELD(dependsOnRole); WRITE_BOOL_FIELD(parallelModeNeeded); + WRITE_BOOL_FIELD(jitFlags); WRITE_NODE_FIELD(planTree); WRITE_NODE_FIELD(rtable); WRITE_NODE_FIELD(resultRelations); diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 068db353d7..fd4586e73d 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1475,6 +1475,7 @@ _readPlannedStmt(void) READ_BOOL_FIELD(transientPlan); READ_BOOL_FIELD(dependsOnRole); READ_BOOL_FIELD(parallelModeNeeded); + READ_BOOL_FIELD(jitFlags); READ_NODE_FIELD(planTree); READ_NODE_FIELD(rtable); READ_NODE_FIELD(resultRelations); diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 54f2da70cb..3668db4e09 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -29,6 +29,7 @@ #include "executor/nodeAgg.h" #include "foreign/fdwapi.h" #include "miscadmin.h" +#include "jit/jit.h" #include "lib/bipartite_match.h" #include "lib/knapsack.h" #include "nodes/makefuncs.h" @@ -531,6 +532,20 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) result->stmt_location = parse->stmt_location; result->stmt_len = parse->stmt_len; + result->jitFlags = PGJIT_NONE; + if (jit_enabled && jit_above_cost >= 0 && + top_plan->total_cost > jit_above_cost) + { + result->jitFlags |= PGJIT_PERFORM; + + /* + * Decide how much effort should be put into generating better code. + */ + if (jit_optimize_above_cost >= 0 && + top_plan->total_cost > jit_optimize_above_cost) + result->jitFlags |= PGJIT_OPT3; + } + return result; } diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 8e09e08306..978c8757c6 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -3075,6 +3075,26 @@ static struct config_real ConfigureNamesReal[] = NULL, NULL, NULL }, + { + {"jit_above_cost", PGC_USERSET, QUERY_TUNING_COST, + gettext_noop("Perform JIT compilation if query is more expensive."), + gettext_noop("-1 disables JIT compilation.") + }, + &jit_above_cost, + 100000, -1, DBL_MAX, + NULL, NULL, NULL + }, + + { + {"jit_optimize_above_cost", PGC_USERSET, QUERY_TUNING_COST, + gettext_noop("Optimize JITed functions if query is more expensive."), + gettext_noop("-1 disables optimization.") + }, + &jit_optimize_above_cost, + 500000, -1, DBL_MAX, + NULL, NULL, NULL + }, + { {"cursor_tuple_fraction", PGC_USERSET, QUERY_TUNING_OTHER, gettext_noop("Sets the planner's estimate of the fraction of " diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index 1e0f411c40..4b692dc3e5 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -318,6 +318,12 @@ #cpu_operator_cost = 0.0025 # same scale as above #parallel_tuple_cost = 0.1 # same scale as above #parallel_setup_cost = 1000.0 # same scale as above + +#jit_above_cost = 100000 # perform JIT compilation if available + # and query more expensive, -1 disables +#jit_optimize_above_cost = 500000 # optimize JITed functions if query is + # more expensive, -1 disables + #min_parallel_table_scan_size = 8MB #min_parallel_index_scan_size = 512kB #effective_cache_size = 4GB diff --git a/src/include/jit/jit.h b/src/include/jit/jit.h index 2c21c2d27b..dac8d593eb 100644 --- a/src/include/jit/jit.h +++ b/src/include/jit/jit.h @@ -61,6 +61,8 @@ extern char *jit_provider; extern bool jit_debugging_support; extern bool jit_dump_bitcode; extern bool jit_profiling_support; +extern double jit_above_cost; +extern double jit_optimize_above_cost; extern void jit_reset_after_error(void); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index d9e591802f..7b752560c6 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -528,6 +528,14 @@ typedef struct EState /* The per-query shared memory area to use for parallel execution. */ struct dsa_area *es_query_dsa; + + /* + * JIT information. es_jit_flags indicates whether JIT should be performed + * and with which options. es_jit is created on-demand when JITing is + * performed. + */ + int es_jit_flags; + struct JitContext *es_jit; } EState; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index f2e19eae68..c922216b7d 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -58,6 +58,8 @@ typedef struct PlannedStmt bool parallelModeNeeded; /* parallel mode required to execute? */ + int jitFlags; /* which forms of JIT should be performed */ + struct Plan *planTree; /* tree of Plan nodes */ List *rtable; /* list of RangeTblEntry nodes */