Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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)