Basic planner and executor integration for JIT.
authorAndres Freund <andres@anarazel.de>
Thu, 22 Mar 2018 18:45:07 +0000 (11:45 -0700)
committerAndres Freund <andres@anarazel.de>
Thu, 22 Mar 2018 18:51:58 +0000 (11:51 -0700)
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

13 files changed:
src/backend/executor/execMain.c
src/backend/executor/execParallel.c
src/backend/executor/execUtils.c
src/backend/jit/jit.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/plan/planner.c
src/backend/utils/misc/guc.c
src/backend/utils/misc/postgresql.conf.sample
src/include/jit/jit.h
src/include/nodes/execnodes.h
src/include/nodes/plannodes.h

index 91ba939bdcade9e58db4f246abdcc215b89da908..890067757c0c8f71de5b7511e0ecf17155481847 100644 (file)
@@ -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
     */
index 14b0b89463cd67fb0a21147301a299bb58620806..52f1a96db5faf24c7c954a9f7182a8929c2e0511 100644 (file)
@@ -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 */
index a8ae37ebc80c2af75f3e255e67d77737ae3cd1ed..14b07b5d449ce0d2f3975d2b85ecf61bd45d3545 100644 (file)
@@ -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
     */
index c17df1c985e1eeafb26ed7f7c2eabfea5854f190..1d74ec8c91a3c21cd94998934a19baa011b3dcfc 100644 (file)
@@ -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;
index 3ad4da64aafd8ce4ada56652ddf2009dfb826968..c7293a60d7863abd4ea05b05729610d8409ae6ee 100644 (file)
@@ -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);
index fd8089195484f3536cc56a479ac698e246bac7c3..f61ae03ac505b475bcb7ee54941bf880bf289607 100644 (file)
@@ -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);
index 068db353d70c590dfabae793cf15b5061e804580..fd4586e73d52d3a6fe865adf0dc6b95129e7270d 100644 (file)
@@ -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);
index 54f2da70cb1c49b70f32e81a343a69a736221547..3668db4e09ee87cd96454a741a5eb69d2d922662 100644 (file)
@@ -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;
 }
 
index 8e09e083069402d66b73b91ee3edff11e673a7a9..978c8757c64bc805f65f0cc94eeb39c020529801 100644 (file)
@@ -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 "
index 1e0f411c4024fc2035aea48ae59ace67142af2b1..4b692dc3e5d1d992803f2166d7bc78816eef2d73 100644 (file)
 #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
index 2c21c2d27b7a5fdb8eb870378ece30a73e70ccde..dac8d593eb08cdf2caefc3d3dc9f47969ad4fe94 100644 (file)
@@ -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);
index d9e591802fefafa8291d2119088b540fc34ba8ae..7b752560c60fdbad4fabdda6eed3593387d9f1d8 100644 (file)
@@ -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;
 
 
index f2e19eae68fceca81e12600e8c914d62eceb880e..c922216b7d152ecb2ef0f6771c2ea9e226bd9209 100644 (file)
@@ -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 */