mirror of
https://github.com/postgres/postgres.git
synced 2025-07-26 01:22:12 +03:00
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
This commit is contained in:
@ -168,6 +168,12 @@ static Datum ExecJustScanVarVirt(ExprState *state, ExprContext *econtext, bool *
|
|||||||
static Datum ExecJustAssignInnerVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
|
static Datum ExecJustAssignInnerVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
|
||||||
static Datum ExecJustAssignOuterVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
|
static Datum ExecJustAssignOuterVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
|
||||||
static Datum ExecJustAssignScanVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
|
static Datum ExecJustAssignScanVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
|
||||||
|
static Datum ExecJustHashInnerVarWithIV(ExprState *state, ExprContext *econtext, bool *isnull);
|
||||||
|
static Datum ExecJustHashOuterVar(ExprState *state, ExprContext *econtext, bool *isnull);
|
||||||
|
static Datum ExecJustHashInnerVar(ExprState *state, ExprContext *econtext, bool *isnull);
|
||||||
|
static Datum ExecJustHashOuterVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
|
||||||
|
static Datum ExecJustHashInnerVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
|
||||||
|
static Datum ExecJustHashOuterVarStrict(ExprState *state, ExprContext *econtext, bool *isnull);
|
||||||
|
|
||||||
/* execution helper functions */
|
/* execution helper functions */
|
||||||
static pg_attribute_always_inline void ExecAggPlainTransByVal(AggState *aggstate,
|
static pg_attribute_always_inline void ExecAggPlainTransByVal(AggState *aggstate,
|
||||||
@ -273,7 +279,51 @@ ExecReadyInterpretedExpr(ExprState *state)
|
|||||||
* the full interpreter is a measurable overhead for these, and these
|
* the full interpreter is a measurable overhead for these, and these
|
||||||
* patterns occur often enough to be worth optimizing.
|
* patterns occur often enough to be worth optimizing.
|
||||||
*/
|
*/
|
||||||
if (state->steps_len == 3)
|
if (state->steps_len == 5)
|
||||||
|
{
|
||||||
|
ExprEvalOp step0 = state->steps[0].opcode;
|
||||||
|
ExprEvalOp step1 = state->steps[1].opcode;
|
||||||
|
ExprEvalOp step2 = state->steps[2].opcode;
|
||||||
|
ExprEvalOp step3 = state->steps[3].opcode;
|
||||||
|
|
||||||
|
if (step0 == EEOP_INNER_FETCHSOME &&
|
||||||
|
step1 == EEOP_HASHDATUM_SET_INITVAL &&
|
||||||
|
step2 == EEOP_INNER_VAR &&
|
||||||
|
step3 == EEOP_HASHDATUM_NEXT32)
|
||||||
|
{
|
||||||
|
state->evalfunc_private = (void *) ExecJustHashInnerVarWithIV;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (state->steps_len == 4)
|
||||||
|
{
|
||||||
|
ExprEvalOp step0 = state->steps[0].opcode;
|
||||||
|
ExprEvalOp step1 = state->steps[1].opcode;
|
||||||
|
ExprEvalOp step2 = state->steps[2].opcode;
|
||||||
|
|
||||||
|
if (step0 == EEOP_OUTER_FETCHSOME &&
|
||||||
|
step1 == EEOP_OUTER_VAR &&
|
||||||
|
step2 == EEOP_HASHDATUM_FIRST)
|
||||||
|
{
|
||||||
|
state->evalfunc_private = (void *) ExecJustHashOuterVar;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (step0 == EEOP_INNER_FETCHSOME &&
|
||||||
|
step1 == EEOP_INNER_VAR &&
|
||||||
|
step2 == EEOP_HASHDATUM_FIRST)
|
||||||
|
{
|
||||||
|
state->evalfunc_private = (void *) ExecJustHashInnerVar;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (step0 == EEOP_OUTER_FETCHSOME &&
|
||||||
|
step1 == EEOP_OUTER_VAR &&
|
||||||
|
step2 == EEOP_HASHDATUM_FIRST_STRICT)
|
||||||
|
{
|
||||||
|
state->evalfunc_private = (void *) ExecJustHashOuterVarStrict;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (state->steps_len == 3)
|
||||||
{
|
{
|
||||||
ExprEvalOp step0 = state->steps[0].opcode;
|
ExprEvalOp step0 = state->steps[0].opcode;
|
||||||
ExprEvalOp step1 = state->steps[1].opcode;
|
ExprEvalOp step1 = state->steps[1].opcode;
|
||||||
@ -321,6 +371,18 @@ ExecReadyInterpretedExpr(ExprState *state)
|
|||||||
state->evalfunc_private = ExecJustApplyFuncToCase;
|
state->evalfunc_private = ExecJustApplyFuncToCase;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if (step0 == EEOP_INNER_VAR &&
|
||||||
|
step1 == EEOP_HASHDATUM_FIRST)
|
||||||
|
{
|
||||||
|
state->evalfunc_private = (void *) ExecJustHashInnerVarVirt;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (step0 == EEOP_OUTER_VAR &&
|
||||||
|
step1 == EEOP_HASHDATUM_FIRST)
|
||||||
|
{
|
||||||
|
state->evalfunc_private = (void *) ExecJustHashOuterVarVirt;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (state->steps_len == 2)
|
else if (state->steps_len == 2)
|
||||||
{
|
{
|
||||||
@ -2484,6 +2546,148 @@ ExecJustAssignScanVarVirt(ExprState *state, ExprContext *econtext, bool *isnull)
|
|||||||
return ExecJustAssignVarVirtImpl(state, econtext->ecxt_scantuple, isnull);
|
return ExecJustAssignVarVirtImpl(state, econtext->ecxt_scantuple, isnull);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* implementation for hashing an inner Var, seeding with an initial value.
|
||||||
|
*/
|
||||||
|
static Datum
|
||||||
|
ExecJustHashInnerVarWithIV(ExprState *state, ExprContext *econtext,
|
||||||
|
bool *isnull)
|
||||||
|
{
|
||||||
|
ExprEvalStep *fetchop = &state->steps[0];
|
||||||
|
ExprEvalStep *setivop = &state->steps[1];
|
||||||
|
ExprEvalStep *innervar = &state->steps[2];
|
||||||
|
ExprEvalStep *hashop = &state->steps[3];
|
||||||
|
FunctionCallInfo fcinfo = hashop->d.hashdatum.fcinfo_data;
|
||||||
|
int attnum = innervar->d.var.attnum;
|
||||||
|
uint32 hashkey;
|
||||||
|
|
||||||
|
CheckOpSlotCompatibility(fetchop, econtext->ecxt_innertuple);
|
||||||
|
slot_getsomeattrs(econtext->ecxt_innertuple, fetchop->d.fetch.last_var);
|
||||||
|
|
||||||
|
fcinfo->args[0].value = econtext->ecxt_innertuple->tts_values[attnum];
|
||||||
|
fcinfo->args[0].isnull = econtext->ecxt_innertuple->tts_isnull[attnum];
|
||||||
|
|
||||||
|
hashkey = DatumGetUInt32(setivop->d.hashdatum_initvalue.init_value);
|
||||||
|
hashkey = pg_rotate_left32(hashkey, 1);
|
||||||
|
|
||||||
|
if (!fcinfo->args[0].isnull)
|
||||||
|
{
|
||||||
|
uint32 hashvalue;
|
||||||
|
|
||||||
|
hashvalue = DatumGetUInt32(hashop->d.hashdatum.fn_addr(fcinfo));
|
||||||
|
hashkey = hashkey ^ hashvalue;
|
||||||
|
}
|
||||||
|
|
||||||
|
*isnull = false;
|
||||||
|
return UInt32GetDatum(hashkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* implementation of ExecJustHash(Inner|Outer)Var */
|
||||||
|
static pg_attribute_always_inline Datum
|
||||||
|
ExecJustHashVarImpl(ExprState *state, TupleTableSlot *slot, bool *isnull)
|
||||||
|
{
|
||||||
|
ExprEvalStep *fetchop = &state->steps[0];
|
||||||
|
ExprEvalStep *var = &state->steps[1];
|
||||||
|
ExprEvalStep *hashop = &state->steps[2];
|
||||||
|
FunctionCallInfo fcinfo = hashop->d.hashdatum.fcinfo_data;
|
||||||
|
int attnum = var->d.var.attnum;
|
||||||
|
|
||||||
|
CheckOpSlotCompatibility(fetchop, slot);
|
||||||
|
slot_getsomeattrs(slot, fetchop->d.fetch.last_var);
|
||||||
|
|
||||||
|
fcinfo->args[0].value = slot->tts_values[attnum];
|
||||||
|
fcinfo->args[0].isnull = slot->tts_isnull[attnum];
|
||||||
|
|
||||||
|
*isnull = false;
|
||||||
|
|
||||||
|
if (!fcinfo->args[0].isnull)
|
||||||
|
return DatumGetUInt32(hashop->d.hashdatum.fn_addr(fcinfo));
|
||||||
|
else
|
||||||
|
return (Datum) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* implementation for hashing an outer Var */
|
||||||
|
static Datum
|
||||||
|
ExecJustHashOuterVar(ExprState *state, ExprContext *econtext, bool *isnull)
|
||||||
|
{
|
||||||
|
return ExecJustHashVarImpl(state, econtext->ecxt_outertuple, isnull);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* implementation for hashing an inner Var */
|
||||||
|
static Datum
|
||||||
|
ExecJustHashInnerVar(ExprState *state, ExprContext *econtext, bool *isnull)
|
||||||
|
{
|
||||||
|
return ExecJustHashVarImpl(state, econtext->ecxt_innertuple, isnull);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* implementation of ExecJustHash(Inner|Outer)VarVirt */
|
||||||
|
static pg_attribute_always_inline Datum
|
||||||
|
ExecJustHashVarVirtImpl(ExprState *state, TupleTableSlot *slot, bool *isnull)
|
||||||
|
{
|
||||||
|
ExprEvalStep *var = &state->steps[0];
|
||||||
|
ExprEvalStep *hashop = &state->steps[1];
|
||||||
|
FunctionCallInfo fcinfo = hashop->d.hashdatum.fcinfo_data;
|
||||||
|
int attnum = var->d.var.attnum;
|
||||||
|
|
||||||
|
fcinfo->args[0].value = slot->tts_values[attnum];
|
||||||
|
fcinfo->args[0].isnull = slot->tts_isnull[attnum];
|
||||||
|
|
||||||
|
*isnull = false;
|
||||||
|
|
||||||
|
if (!fcinfo->args[0].isnull)
|
||||||
|
return DatumGetUInt32(hashop->d.hashdatum.fn_addr(fcinfo));
|
||||||
|
else
|
||||||
|
return (Datum) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Like ExecJustHashInnerVar, optimized for virtual slots */
|
||||||
|
static Datum
|
||||||
|
ExecJustHashInnerVarVirt(ExprState *state, ExprContext *econtext,
|
||||||
|
bool *isnull)
|
||||||
|
{
|
||||||
|
return ExecJustHashVarVirtImpl(state, econtext->ecxt_innertuple, isnull);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Like ExecJustHashOuterVar, optimized for virtual slots */
|
||||||
|
static Datum
|
||||||
|
ExecJustHashOuterVarVirt(ExprState *state, ExprContext *econtext,
|
||||||
|
bool *isnull)
|
||||||
|
{
|
||||||
|
return ExecJustHashVarVirtImpl(state, econtext->ecxt_outertuple, isnull);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* implementation for hashing an outer Var. Returns NULL on NULL input.
|
||||||
|
*/
|
||||||
|
static Datum
|
||||||
|
ExecJustHashOuterVarStrict(ExprState *state, ExprContext *econtext,
|
||||||
|
bool *isnull)
|
||||||
|
{
|
||||||
|
ExprEvalStep *fetchop = &state->steps[0];
|
||||||
|
ExprEvalStep *var = &state->steps[1];
|
||||||
|
ExprEvalStep *hashop = &state->steps[2];
|
||||||
|
FunctionCallInfo fcinfo = hashop->d.hashdatum.fcinfo_data;
|
||||||
|
int attnum = var->d.var.attnum;
|
||||||
|
|
||||||
|
CheckOpSlotCompatibility(fetchop, econtext->ecxt_outertuple);
|
||||||
|
slot_getsomeattrs(econtext->ecxt_outertuple, fetchop->d.fetch.last_var);
|
||||||
|
|
||||||
|
fcinfo->args[0].value = econtext->ecxt_outertuple->tts_values[attnum];
|
||||||
|
fcinfo->args[0].isnull = econtext->ecxt_outertuple->tts_isnull[attnum];
|
||||||
|
|
||||||
|
if (!fcinfo->args[0].isnull)
|
||||||
|
{
|
||||||
|
*isnull = false;
|
||||||
|
return DatumGetUInt32(hashop->d.hashdatum.fn_addr(fcinfo));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* return NULL on NULL input */
|
||||||
|
*isnull = true;
|
||||||
|
return (Datum) 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(EEO_USE_COMPUTED_GOTO)
|
#if defined(EEO_USE_COMPUTED_GOTO)
|
||||||
/*
|
/*
|
||||||
* Comparator used when building address->opcode lookup table for
|
* Comparator used when building address->opcode lookup table for
|
||||||
|
Reference in New Issue
Block a user