Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 6c82d8d

Browse files
committed
Further reduce overhead for passing plpgsql variables to the executor.
This builds on commit 21dcda2 by keeping a plpgsql function's shared ParamListInfo's entries for simple variables (PLPGSQL_DTYPE_VARs) valid at all times. That adds a few cycles to each assignment to such variables, but saves significantly more cycles each time they are used; so except in the pathological case of many dead stores, this should always be a win. Initial testing says it's good for about a 10% speedup of simple calculations; more in large functions with many datums. We can't use this method for row/record references unfortunately, so what we do for those is reset those ParamListInfo slots after use; which we can skip doing unless some of them were actually evaluated during the previous evaluation call. So this should frequently be a win as well, while worst case is that it's similar cost to the previous approach. Also, closer study suggests that the previous method of instantiating a new ParamListInfo array per evaluation is actually probably optimal for cursor-opening executor calls. The reason is that whatever is visible in the array is going to get copied into the cursor portal via copyParamList. So if we used the function's main ParamListInfo for those calls, we'd end up with all of its DTYPE_VAR vars getting copied, which might well include large pass-by-reference values that the cursor actually has no need for. To avoid a possible net degradation in cursor cases, go back to creating and filling a private ParamListInfo in those cases (which therefore will be exactly the same speed as before 21dcda2). We still get some benefit out of this though, because this approach means that we only have to defend against copyParamList's try-to-fetch-every-slot behavior in the case of an unshared ParamListInfo; so plpgsql_param_fetch() can skip testing expr->paramnos in the common case. To ensure that the main ParamListInfo's image of a DTYPE_VAR datum is always valid, all assignments to such variables are now funneled through assign_simple_var(). But this makes for cleaner and shorter code anyway.
1 parent 2524046 commit 6c82d8d

File tree

3 files changed

+359
-207
lines changed

3 files changed

+359
-207
lines changed

src/pl/plpgsql/src/pl_comp.c

+59-22
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ PLpgSQL_stmt_block *plpgsql_parse_result;
4242
static int datums_alloc;
4343
int plpgsql_nDatums;
4444
PLpgSQL_datum **plpgsql_Datums;
45-
static int datums_last = 0;
45+
static int datums_last;
4646

4747
char *plpgsql_error_funcname;
4848
bool plpgsql_DumpExecTree = false;
@@ -104,6 +104,8 @@ static Node *make_datum_param(PLpgSQL_expr *expr, int dno, int location);
104104
static PLpgSQL_row *build_row_from_class(Oid classOid);
105105
static PLpgSQL_row *build_row_from_vars(PLpgSQL_variable **vars, int numvars);
106106
static PLpgSQL_type *build_datatype(HeapTuple typeTup, int32 typmod, Oid collation);
107+
static void plpgsql_start_datums(void);
108+
static void plpgsql_finish_datums(PLpgSQL_function *function);
107109
static void compute_function_hashkey(FunctionCallInfo fcinfo,
108110
Form_pg_proc procStruct,
109111
PLpgSQL_func_hashkey *hashkey,
@@ -371,13 +373,7 @@ do_compile(FunctionCallInfo fcinfo,
371373
plpgsql_ns_init();
372374
plpgsql_ns_push(NameStr(procStruct->proname));
373375
plpgsql_DumpExecTree = false;
374-
375-
datums_alloc = 128;
376-
plpgsql_nDatums = 0;
377-
/* This is short-lived, so needn't allocate in function's cxt */
378-
plpgsql_Datums = MemoryContextAlloc(compile_tmp_cxt,
379-
sizeof(PLpgSQL_datum *) * datums_alloc);
380-
datums_last = 0;
376+
plpgsql_start_datums();
381377

382378
switch (function->fn_is_trigger)
383379
{
@@ -758,10 +754,8 @@ do_compile(FunctionCallInfo fcinfo,
758754
function->fn_nargs = procStruct->pronargs;
759755
for (i = 0; i < function->fn_nargs; i++)
760756
function->fn_argvarnos[i] = in_arg_varnos[i];
761-
function->ndatums = plpgsql_nDatums;
762-
function->datums = palloc(sizeof(PLpgSQL_datum *) * plpgsql_nDatums);
763-
for (i = 0; i < plpgsql_nDatums; i++)
764-
function->datums[i] = plpgsql_Datums[i];
757+
758+
plpgsql_finish_datums(function);
765759

766760
/* Debug dump for completed functions */
767761
if (plpgsql_DumpExecTree)
@@ -804,7 +798,6 @@ plpgsql_compile_inline(char *proc_source)
804798
PLpgSQL_variable *var;
805799
int parse_rc;
806800
MemoryContext func_cxt;
807-
int i;
808801

809802
/*
810803
* Setup the scanner input and error info. We assume that this function
@@ -860,11 +853,7 @@ plpgsql_compile_inline(char *proc_source)
860853
plpgsql_ns_init();
861854
plpgsql_ns_push(func_name);
862855
plpgsql_DumpExecTree = false;
863-
864-
datums_alloc = 128;
865-
plpgsql_nDatums = 0;
866-
plpgsql_Datums = palloc(sizeof(PLpgSQL_datum *) * datums_alloc);
867-
datums_last = 0;
856+
plpgsql_start_datums();
868857

869858
/* Set up as though in a function returning VOID */
870859
function->fn_rettype = VOIDOID;
@@ -911,10 +900,8 @@ plpgsql_compile_inline(char *proc_source)
911900
* Complete the function's info
912901
*/
913902
function->fn_nargs = 0;
914-
function->ndatums = plpgsql_nDatums;
915-
function->datums = palloc(sizeof(PLpgSQL_datum *) * plpgsql_nDatums);
916-
for (i = 0; i < plpgsql_nDatums; i++)
917-
function->datums[i] = plpgsql_Datums[i];
903+
904+
plpgsql_finish_datums(function);
918905

919906
/*
920907
* Pop the error context stack
@@ -1965,6 +1952,7 @@ plpgsql_build_record(const char *refname, int lineno, bool add2namespace)
19651952
rec->tup = NULL;
19661953
rec->tupdesc = NULL;
19671954
rec->freetup = false;
1955+
rec->freetupdesc = false;
19681956
plpgsql_adddatum((PLpgSQL_datum *) rec);
19691957
if (add2namespace)
19701958
plpgsql_ns_additem(PLPGSQL_NSTYPE_REC, rec->dno, rec->refname);
@@ -2311,6 +2299,22 @@ plpgsql_parse_err_condition(char *condname)
23112299
return prev;
23122300
}
23132301

2302+
/* ----------
2303+
* plpgsql_start_datums Initialize datum list at compile startup.
2304+
* ----------
2305+
*/
2306+
static void
2307+
plpgsql_start_datums(void)
2308+
{
2309+
datums_alloc = 128;
2310+
plpgsql_nDatums = 0;
2311+
/* This is short-lived, so needn't allocate in function's cxt */
2312+
plpgsql_Datums = MemoryContextAlloc(compile_tmp_cxt,
2313+
sizeof(PLpgSQL_datum *) * datums_alloc);
2314+
/* datums_last tracks what's been seen by plpgsql_add_initdatums() */
2315+
datums_last = 0;
2316+
}
2317+
23142318
/* ----------
23152319
* plpgsql_adddatum Add a variable, record or row
23162320
* to the compiler's datum list.
@@ -2329,6 +2333,39 @@ plpgsql_adddatum(PLpgSQL_datum *new)
23292333
plpgsql_Datums[plpgsql_nDatums++] = new;
23302334
}
23312335

2336+
/* ----------
2337+
* plpgsql_finish_datums Copy completed datum info into function struct.
2338+
*
2339+
* This is also responsible for building resettable_datums, a bitmapset
2340+
* of the dnos of all ROW, REC, and RECFIELD datums in the function.
2341+
* ----------
2342+
*/
2343+
static void
2344+
plpgsql_finish_datums(PLpgSQL_function *function)
2345+
{
2346+
Bitmapset *resettable_datums = NULL;
2347+
int i;
2348+
2349+
function->ndatums = plpgsql_nDatums;
2350+
function->datums = palloc(sizeof(PLpgSQL_datum *) * plpgsql_nDatums);
2351+
for (i = 0; i < plpgsql_nDatums; i++)
2352+
{
2353+
function->datums[i] = plpgsql_Datums[i];
2354+
switch (function->datums[i]->dtype)
2355+
{
2356+
case PLPGSQL_DTYPE_ROW:
2357+
case PLPGSQL_DTYPE_REC:
2358+
case PLPGSQL_DTYPE_RECFIELD:
2359+
resettable_datums = bms_add_member(resettable_datums, i);
2360+
break;
2361+
2362+
default:
2363+
break;
2364+
}
2365+
}
2366+
function->resettable_datums = resettable_datums;
2367+
}
2368+
23322369

23332370
/* ----------
23342371
* plpgsql_add_initdatums Make an array of the datum numbers of

0 commit comments

Comments
 (0)