Skip to content

Commit 50416cc

Browse files
committed
Speedup Hash Joins with dedicated functions for ExprState hashing
Hashing of a single Var is a very common operation for ExprState to perform. Here we add dedicated ExecJust* functions which helps speed up Hash Joins by removing the interpretation overhead in ExecInterpExpr(). This change currently only affects Hash Joins on a single column. Hash Joins with multiple join keys or an expression still run through ExecInterpExpr(). Some testing has shown up to 10% query performance increases on recent AMD hardware and nearly 7% increase on an Apple M2 for a query performing a hash join with a large number of lookups on a small hash table. This change was extracted from a larger patch which adjusts GROUP BY / hashed subplans / hashed set operations to use ExprState hashing. Discussion: https://postgr.es/m/CAApHDvr8Zc0ZgzVoCZLdHGOFNhiJeQ6vrUcS9V7N23zMWQb-eA@mail.gmail.com
1 parent 9828905 commit 50416cc

File tree

1 file changed

+205
-1
lines changed

1 file changed

+205
-1
lines changed

src/backend/executor/execExprInterp.c

Lines changed: 205 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,12 @@ static Datum ExecJustScanVarVirt(ExprState *state, ExprContext *econtext, bool *
168168
static Datum ExecJustAssignInnerVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
169169
static Datum ExecJustAssignOuterVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
170170
static Datum ExecJustAssignScanVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
171+
static Datum ExecJustHashInnerVarWithIV(ExprState *state, ExprContext *econtext, bool *isnull);
172+
static Datum ExecJustHashOuterVar(ExprState *state, ExprContext *econtext, bool *isnull);
173+
static Datum ExecJustHashInnerVar(ExprState *state, ExprContext *econtext, bool *isnull);
174+
static Datum ExecJustHashOuterVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
175+
static Datum ExecJustHashInnerVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
176+
static Datum ExecJustHashOuterVarStrict(ExprState *state, ExprContext *econtext, bool *isnull);
171177

172178
/* execution helper functions */
173179
static pg_attribute_always_inline void ExecAggPlainTransByVal(AggState *aggstate,
@@ -273,7 +279,51 @@ ExecReadyInterpretedExpr(ExprState *state)
273279
* the full interpreter is a measurable overhead for these, and these
274280
* patterns occur often enough to be worth optimizing.
275281
*/
276-
if (state->steps_len == 3)
282+
if (state->steps_len == 5)
283+
{
284+
ExprEvalOp step0 = state->steps[0].opcode;
285+
ExprEvalOp step1 = state->steps[1].opcode;
286+
ExprEvalOp step2 = state->steps[2].opcode;
287+
ExprEvalOp step3 = state->steps[3].opcode;
288+
289+
if (step0 == EEOP_INNER_FETCHSOME &&
290+
step1 == EEOP_HASHDATUM_SET_INITVAL &&
291+
step2 == EEOP_INNER_VAR &&
292+
step3 == EEOP_HASHDATUM_NEXT32)
293+
{
294+
state->evalfunc_private = (void *) ExecJustHashInnerVarWithIV;
295+
return;
296+
}
297+
}
298+
else if (state->steps_len == 4)
299+
{
300+
ExprEvalOp step0 = state->steps[0].opcode;
301+
ExprEvalOp step1 = state->steps[1].opcode;
302+
ExprEvalOp step2 = state->steps[2].opcode;
303+
304+
if (step0 == EEOP_OUTER_FETCHSOME &&
305+
step1 == EEOP_OUTER_VAR &&
306+
step2 == EEOP_HASHDATUM_FIRST)
307+
{
308+
state->evalfunc_private = (void *) ExecJustHashOuterVar;
309+
return;
310+
}
311+
else if (step0 == EEOP_INNER_FETCHSOME &&
312+
step1 == EEOP_INNER_VAR &&
313+
step2 == EEOP_HASHDATUM_FIRST)
314+
{
315+
state->evalfunc_private = (void *) ExecJustHashInnerVar;
316+
return;
317+
}
318+
else if (step0 == EEOP_OUTER_FETCHSOME &&
319+
step1 == EEOP_OUTER_VAR &&
320+
step2 == EEOP_HASHDATUM_FIRST_STRICT)
321+
{
322+
state->evalfunc_private = (void *) ExecJustHashOuterVarStrict;
323+
return;
324+
}
325+
}
326+
else if (state->steps_len == 3)
277327
{
278328
ExprEvalOp step0 = state->steps[0].opcode;
279329
ExprEvalOp step1 = state->steps[1].opcode;
@@ -321,6 +371,18 @@ ExecReadyInterpretedExpr(ExprState *state)
321371
state->evalfunc_private = ExecJustApplyFuncToCase;
322372
return;
323373
}
374+
else if (step0 == EEOP_INNER_VAR &&
375+
step1 == EEOP_HASHDATUM_FIRST)
376+
{
377+
state->evalfunc_private = (void *) ExecJustHashInnerVarVirt;
378+
return;
379+
}
380+
else if (step0 == EEOP_OUTER_VAR &&
381+
step1 == EEOP_HASHDATUM_FIRST)
382+
{
383+
state->evalfunc_private = (void *) ExecJustHashOuterVarVirt;
384+
return;
385+
}
324386
}
325387
else if (state->steps_len == 2)
326388
{
@@ -2484,6 +2546,148 @@ ExecJustAssignScanVarVirt(ExprState *state, ExprContext *econtext, bool *isnull)
24842546
return ExecJustAssignVarVirtImpl(state, econtext->ecxt_scantuple, isnull);
24852547
}
24862548

2549+
/*
2550+
* implementation for hashing an inner Var, seeding with an initial value.
2551+
*/
2552+
static Datum
2553+
ExecJustHashInnerVarWithIV(ExprState *state, ExprContext *econtext,
2554+
bool *isnull)
2555+
{
2556+
ExprEvalStep *fetchop = &state->steps[0];
2557+
ExprEvalStep *setivop = &state->steps[1];
2558+
ExprEvalStep *innervar = &state->steps[2];
2559+
ExprEvalStep *hashop = &state->steps[3];
2560+
FunctionCallInfo fcinfo = hashop->d.hashdatum.fcinfo_data;
2561+
int attnum = innervar->d.var.attnum;
2562+
uint32 hashkey;
2563+
2564+
CheckOpSlotCompatibility(fetchop, econtext->ecxt_innertuple);
2565+
slot_getsomeattrs(econtext->ecxt_innertuple, fetchop->d.fetch.last_var);
2566+
2567+
fcinfo->args[0].value = econtext->ecxt_innertuple->tts_values[attnum];
2568+
fcinfo->args[0].isnull = econtext->ecxt_innertuple->tts_isnull[attnum];
2569+
2570+
hashkey = DatumGetUInt32(setivop->d.hashdatum_initvalue.init_value);
2571+
hashkey = pg_rotate_left32(hashkey, 1);
2572+
2573+
if (!fcinfo->args[0].isnull)
2574+
{
2575+
uint32 hashvalue;
2576+
2577+
hashvalue = DatumGetUInt32(hashop->d.hashdatum.fn_addr(fcinfo));
2578+
hashkey = hashkey ^ hashvalue;
2579+
}
2580+
2581+
*isnull = false;
2582+
return UInt32GetDatum(hashkey);
2583+
}
2584+
2585+
/* implementation of ExecJustHash(Inner|Outer)Var */
2586+
static pg_attribute_always_inline Datum
2587+
ExecJustHashVarImpl(ExprState *state, TupleTableSlot *slot, bool *isnull)
2588+
{
2589+
ExprEvalStep *fetchop = &state->steps[0];
2590+
ExprEvalStep *var = &state->steps[1];
2591+
ExprEvalStep *hashop = &state->steps[2];
2592+
FunctionCallInfo fcinfo = hashop->d.hashdatum.fcinfo_data;
2593+
int attnum = var->d.var.attnum;
2594+
2595+
CheckOpSlotCompatibility(fetchop, slot);
2596+
slot_getsomeattrs(slot, fetchop->d.fetch.last_var);
2597+
2598+
fcinfo->args[0].value = slot->tts_values[attnum];
2599+
fcinfo->args[0].isnull = slot->tts_isnull[attnum];
2600+
2601+
*isnull = false;
2602+
2603+
if (!fcinfo->args[0].isnull)
2604+
return DatumGetUInt32(hashop->d.hashdatum.fn_addr(fcinfo));
2605+
else
2606+
return (Datum) 0;
2607+
}
2608+
2609+
/* implementation for hashing an outer Var */
2610+
static Datum
2611+
ExecJustHashOuterVar(ExprState *state, ExprContext *econtext, bool *isnull)
2612+
{
2613+
return ExecJustHashVarImpl(state, econtext->ecxt_outertuple, isnull);
2614+
}
2615+
2616+
/* implementation for hashing an inner Var */
2617+
static Datum
2618+
ExecJustHashInnerVar(ExprState *state, ExprContext *econtext, bool *isnull)
2619+
{
2620+
return ExecJustHashVarImpl(state, econtext->ecxt_innertuple, isnull);
2621+
}
2622+
2623+
/* implementation of ExecJustHash(Inner|Outer)VarVirt */
2624+
static pg_attribute_always_inline Datum
2625+
ExecJustHashVarVirtImpl(ExprState *state, TupleTableSlot *slot, bool *isnull)
2626+
{
2627+
ExprEvalStep *var = &state->steps[0];
2628+
ExprEvalStep *hashop = &state->steps[1];
2629+
FunctionCallInfo fcinfo = hashop->d.hashdatum.fcinfo_data;
2630+
int attnum = var->d.var.attnum;
2631+
2632+
fcinfo->args[0].value = slot->tts_values[attnum];
2633+
fcinfo->args[0].isnull = slot->tts_isnull[attnum];
2634+
2635+
*isnull = false;
2636+
2637+
if (!fcinfo->args[0].isnull)
2638+
return DatumGetUInt32(hashop->d.hashdatum.fn_addr(fcinfo));
2639+
else
2640+
return (Datum) 0;
2641+
}
2642+
2643+
/* Like ExecJustHashInnerVar, optimized for virtual slots */
2644+
static Datum
2645+
ExecJustHashInnerVarVirt(ExprState *state, ExprContext *econtext,
2646+
bool *isnull)
2647+
{
2648+
return ExecJustHashVarVirtImpl(state, econtext->ecxt_innertuple, isnull);
2649+
}
2650+
2651+
/* Like ExecJustHashOuterVar, optimized for virtual slots */
2652+
static Datum
2653+
ExecJustHashOuterVarVirt(ExprState *state, ExprContext *econtext,
2654+
bool *isnull)
2655+
{
2656+
return ExecJustHashVarVirtImpl(state, econtext->ecxt_outertuple, isnull);
2657+
}
2658+
2659+
/*
2660+
* implementation for hashing an outer Var. Returns NULL on NULL input.
2661+
*/
2662+
static Datum
2663+
ExecJustHashOuterVarStrict(ExprState *state, ExprContext *econtext,
2664+
bool *isnull)
2665+
{
2666+
ExprEvalStep *fetchop = &state->steps[0];
2667+
ExprEvalStep *var = &state->steps[1];
2668+
ExprEvalStep *hashop = &state->steps[2];
2669+
FunctionCallInfo fcinfo = hashop->d.hashdatum.fcinfo_data;
2670+
int attnum = var->d.var.attnum;
2671+
2672+
CheckOpSlotCompatibility(fetchop, econtext->ecxt_outertuple);
2673+
slot_getsomeattrs(econtext->ecxt_outertuple, fetchop->d.fetch.last_var);
2674+
2675+
fcinfo->args[0].value = econtext->ecxt_outertuple->tts_values[attnum];
2676+
fcinfo->args[0].isnull = econtext->ecxt_outertuple->tts_isnull[attnum];
2677+
2678+
if (!fcinfo->args[0].isnull)
2679+
{
2680+
*isnull = false;
2681+
return DatumGetUInt32(hashop->d.hashdatum.fn_addr(fcinfo));
2682+
}
2683+
else
2684+
{
2685+
/* return NULL on NULL input */
2686+
*isnull = true;
2687+
return (Datum) 0;
2688+
}
2689+
}
2690+
24872691
#if defined(EEO_USE_COMPUTED_GOTO)
24882692
/*
24892693
* Comparator used when building address->opcode lookup table for

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy