From da7a1dc0d62ac3141328f4e6ad51d70e918167aa Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 2 Feb 2026 14:39:50 -0500 Subject: [PATCH] Refactor att_align_nominal() to improve performance. Separate att_align_nominal() into two macros, similarly to what was already done with att_align_datum() and att_align_pointer(). The inner macro att_nominal_alignby() is really just TYPEALIGN(), while att_align_nominal() retains its previous API by mapping TYPALIGN_xxx values to numbers of bytes to align to and then calling att_nominal_alignby(). In support of this, split out tupdesc.c's logic to do that mapping into a publicly visible function typalign_to_alignby(). Having done that, we can replace performance-critical uses of att_align_nominal() with att_nominal_alignby(), where the typalign_to_alignby() mapping is done just once outside the loop. In most places I settled for doing typalign_to_alignby() once per function. We could in many places pass the alignby value in from the caller if we wanted to change function APIs for this purpose; but I'm a bit loath to do that, especially for exported APIs that extensions might call. Replacing a char typalign argument by a uint8 typalignby argument would be an API change that compilers would fail to warn about, thus silently breaking code in hard-to-debug ways. I did revise the APIs of array_iter_setup and array_iter_next, moving the element type attribute arguments to the former; if any external code uses those, the argument-count change will cause visible compile failures. Performance testing shows that ExecEvalScalarArrayOp is sped up by about 10% by this change, when using a simple per-element function such as int8eq. I did not check any of the other loops optimized here, but it's reasonable to expect similar gains. Although the motivation for creating this patch was to avoid a performance loss if we add some more typalign values, it evidently is worth doing whether that patch lands or not. Discussion: https://postgr.es/m/1127261.1769649624@sss.pgh.pa.us --- contrib/dblink/dblink.c | 4 +- src/backend/access/common/tupdesc.c | 21 +--- src/backend/executor/execExprInterp.c | 8 +- src/backend/utils/adt/array_expanded.c | 4 +- src/backend/utils/adt/arrayfuncs.c | 149 +++++++++++++----------- src/backend/utils/adt/multirangetypes.c | 16 +-- src/backend/utils/adt/varlena.c | 4 +- src/include/access/tupmacs.h | 51 ++++++-- src/include/utils/arrayaccess.h | 25 ++-- src/pl/plpython/plpy_typeio.c | 3 +- 10 files changed, 166 insertions(+), 119 deletions(-) diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c index 8cb3166495c..2498d80c8e7 100644 --- a/contrib/dblink/dblink.c +++ b/contrib/dblink/dblink.c @@ -2069,6 +2069,7 @@ get_text_array_contents(ArrayType *array, int *numitems) int16 typlen; bool typbyval; char typalign; + uint8 typalignby; char **values; char *ptr; bits8 *bitmap; @@ -2081,6 +2082,7 @@ get_text_array_contents(ArrayType *array, int *numitems) get_typlenbyvalalign(ARR_ELEMTYPE(array), &typlen, &typbyval, &typalign); + typalignby = typalign_to_alignby(typalign); values = palloc_array(char *, nitems); @@ -2098,7 +2100,7 @@ get_text_array_contents(ArrayType *array, int *numitems) { values[i] = TextDatumGetCString(PointerGetDatum(ptr)); ptr = att_addlength_pointer(ptr, typlen, ptr); - ptr = (char *) att_align_nominal(ptr, typalign); + ptr = (char *) att_nominal_alignby(ptr, typalignby); } /* advance bitmap pointer if any */ diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c index 94b4f1f9975..b69d10f0a45 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -86,25 +86,8 @@ populate_compact_attribute_internal(Form_pg_attribute src, IsCatalogRelationOid(src->attrelid) ? ATTNULLABLE_VALID : ATTNULLABLE_UNKNOWN; - switch (src->attalign) - { - case TYPALIGN_INT: - dst->attalignby = ALIGNOF_INT; - break; - case TYPALIGN_CHAR: - dst->attalignby = sizeof(char); - break; - case TYPALIGN_DOUBLE: - dst->attalignby = ALIGNOF_DOUBLE; - break; - case TYPALIGN_SHORT: - dst->attalignby = ALIGNOF_SHORT; - break; - default: - dst->attalignby = 0; - elog(ERROR, "invalid attalign value: %c", src->attalign); - break; - } + /* Compute numeric alignment requirement, too */ + dst->attalignby = typalign_to_alignby(src->attalign); } /* diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index a7a5ac1e83b..61ff5ddc74c 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -4032,6 +4032,7 @@ ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op) int16 typlen; bool typbyval; char typalign; + uint8 typalignby; char *s; bits8 *bitmap; int bitmask; @@ -4086,6 +4087,7 @@ ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op) typlen = op->d.scalararrayop.typlen; typbyval = op->d.scalararrayop.typbyval; typalign = op->d.scalararrayop.typalign; + typalignby = typalign_to_alignby(typalign); /* Initialize result appropriately depending on useOr */ result = BoolGetDatum(!useOr); @@ -4111,7 +4113,7 @@ ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op) { elt = fetch_att(s, typbyval, typlen); s = att_addlength_pointer(s, typlen, s); - s = (char *) att_align_nominal(s, typalign); + s = (char *) att_nominal_alignby(s, typalignby); fcinfo->args[1].value = elt; fcinfo->args[1].isnull = false; } @@ -4255,6 +4257,7 @@ ExecEvalHashedScalarArrayOp(ExprState *state, ExprEvalStep *op, ExprContext *eco int16 typlen; bool typbyval; char typalign; + uint8 typalignby; int nitems; bool has_nulls = false; char *s; @@ -4272,6 +4275,7 @@ ExecEvalHashedScalarArrayOp(ExprState *state, ExprEvalStep *op, ExprContext *eco &typlen, &typbyval, &typalign); + typalignby = typalign_to_alignby(typalign); oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); @@ -4318,7 +4322,7 @@ ExecEvalHashedScalarArrayOp(ExprState *state, ExprEvalStep *op, ExprContext *eco element = fetch_att(s, typbyval, typlen); s = att_addlength_pointer(s, typlen, s); - s = (char *) att_align_nominal(s, typalign); + s = (char *) att_nominal_alignby(s, typalignby); saophash_insert(elements_tab->hashtab, element, &hashfound); } diff --git a/src/backend/utils/adt/array_expanded.c b/src/backend/utils/adt/array_expanded.c index 01e3dddcbbb..7e8352af52b 100644 --- a/src/backend/utils/adt/array_expanded.c +++ b/src/backend/utils/adt/array_expanded.c @@ -238,6 +238,7 @@ EA_get_flat_size(ExpandedObjectHeader *eohptr) Datum *dvalues; bool *dnulls; Size nbytes; + uint8 typalignby; int i; Assert(eah->ea_magic == EA_MAGIC); @@ -261,12 +262,13 @@ EA_get_flat_size(ExpandedObjectHeader *eohptr) dvalues = eah->dvalues; dnulls = eah->dnulls; nbytes = 0; + typalignby = typalign_to_alignby(eah->typalign); for (i = 0; i < nelems; i++) { if (dnulls && dnulls[i]) continue; nbytes = att_addlength_datum(nbytes, eah->typlen, dvalues[i]); - nbytes = att_align_nominal(nbytes, eah->typalign); + nbytes = att_nominal_alignby(nbytes, typalignby); /* check for overflow of total request */ if (!AllocSizeIsValid(nbytes)) ereport(ERROR, diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index e71d32773b5..da68915ee20 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -75,6 +75,7 @@ typedef struct ArrayIteratorData int16 typlen; /* element type's length */ bool typbyval; /* element type's byval property */ char typalign; /* element type's align property */ + uint8 typalignby; /* typalign mapped to numeric alignment */ /* information about the requested slice size */ int slice_ndim; /* slice dimension, or 0 if not slicing */ @@ -123,7 +124,7 @@ static bool array_get_isnull(const bits8 *nullbitmap, int offset); static void array_set_isnull(bits8 *nullbitmap, int offset, bool isNull); static Datum ArrayCast(char *value, bool byval, int len); static int ArrayCastAndSet(Datum src, - int typlen, bool typbyval, char typalign, + int typlen, bool typbyval, uint8 typalignby, char *dest); static char *array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems, int typlen, bool typbyval, char typalign); @@ -187,6 +188,7 @@ array_in(PG_FUNCTION_ARGS) int typlen; bool typbyval; char typalign; + uint8 typalignby; char typdelim; Oid typioparam; char *p; @@ -232,6 +234,7 @@ array_in(PG_FUNCTION_ARGS) typlen = my_extra->typlen; typbyval = my_extra->typbyval; typalign = my_extra->typalign; + typalignby = typalign_to_alignby(typalign); typdelim = my_extra->typdelim; typioparam = my_extra->typioparam; @@ -328,7 +331,7 @@ array_in(PG_FUNCTION_ARGS) if (typlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i])); nbytes = att_addlength_datum(nbytes, typlen, values[i]); - nbytes = att_align_nominal(nbytes, typalign); + nbytes = att_nominal_alignby(nbytes, typalignby); /* check for overflow of total request */ if (!AllocSizeIsValid(nbytes)) ereturn(escontext, (Datum) 0, @@ -972,6 +975,7 @@ CopyArrayEls(ArrayType *array, bits8 *bitmap = ARR_NULLBITMAP(array); int bitval = 0; int bitmask = 1; + uint8 typalignby = typalign_to_alignby(typalign); int i; if (typbyval) @@ -988,7 +992,7 @@ CopyArrayEls(ArrayType *array, else { bitval |= bitmask; - p += ArrayCastAndSet(values[i], typlen, typbyval, typalign, p); + p += ArrayCastAndSet(values[i], typlen, typbyval, typalignby, p); if (freedata) pfree(DatumGetPointer(values[i])); } @@ -1112,7 +1116,7 @@ array_out(PG_FUNCTION_ARGS) needquotes = (bool *) palloc(nitems * sizeof(bool)); overall_length = 0; - array_iter_setup(&iter, v); + array_iter_setup(&iter, v, typlen, typbyval, typalign); for (i = 0; i < nitems; i++) { @@ -1121,8 +1125,7 @@ array_out(PG_FUNCTION_ARGS) bool needquote; /* Get source element, checking for NULL */ - itemvalue = array_iter_next(&iter, &isnull, i, - typlen, typbyval, typalign); + itemvalue = array_iter_next(&iter, &isnull, i); if (isnull) { @@ -1468,6 +1471,7 @@ ReadArrayBinary(StringInfo buf, int i; bool hasnull; int32 totbytes; + uint8 typalignby = typalign_to_alignby(typalign); for (i = 0; i < nitems; i++) { @@ -1526,7 +1530,7 @@ ReadArrayBinary(StringInfo buf, if (typlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i])); totbytes = att_addlength_datum(totbytes, typlen, values[i]); - totbytes = att_align_nominal(totbytes, typalign); + totbytes = att_nominal_alignby(totbytes, typalignby); /* check for overflow of total request */ if (!AllocSizeIsValid(totbytes)) ereport(ERROR, @@ -1614,7 +1618,7 @@ array_send(PG_FUNCTION_ARGS) } /* Send the array elements using the element's own sendproc */ - array_iter_setup(&iter, v); + array_iter_setup(&iter, v, typlen, typbyval, typalign); for (i = 0; i < nitems; i++) { @@ -1622,8 +1626,7 @@ array_send(PG_FUNCTION_ARGS) bool isnull; /* Get source element, checking for NULL */ - itemvalue = array_iter_next(&iter, &isnull, i, - typlen, typbyval, typalign); + itemvalue = array_iter_next(&iter, &isnull, i); if (isnull) { @@ -2231,6 +2234,7 @@ array_set_element(Datum arraydatum, addedafter, lenbefore, lenafter; + uint8 elmalignby = typalign_to_alignby(elmalign); if (arraytyplen > 0) { @@ -2258,7 +2262,7 @@ array_set_element(Datum arraydatum, resultarray = (char *) palloc(arraytyplen); memcpy(resultarray, DatumGetPointer(arraydatum), arraytyplen); elt_ptr = resultarray + indx[0] * elmlen; - ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, elt_ptr); + ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalignby, elt_ptr); return PointerGetDatum(resultarray); } @@ -2416,7 +2420,7 @@ array_set_element(Datum arraydatum, else { olditemlen = att_addlength_pointer(0, elmlen, elt_ptr); - olditemlen = att_align_nominal(olditemlen, elmalign); + olditemlen = att_nominal_alignby(olditemlen, elmalignby); } lenafter = olddatasize - lenbefore - olditemlen; } @@ -2426,7 +2430,7 @@ array_set_element(Datum arraydatum, else { newitemlen = att_addlength_datum(0, elmlen, dataValue); - newitemlen = att_align_nominal(newitemlen, elmalign); + newitemlen = att_nominal_alignby(newitemlen, elmalignby); } newsize = overheadlen + lenbefore + newitemlen + lenafter; @@ -2449,7 +2453,7 @@ array_set_element(Datum arraydatum, (char *) array + oldoverheadlen, lenbefore); if (!isNull) - ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, + ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalignby, (char *) newarray + overheadlen + lenbefore); memcpy((char *) newarray + overheadlen + lenbefore + newitemlen, (char *) array + oldoverheadlen + lenbefore + olditemlen, @@ -3221,6 +3225,7 @@ array_map(Datum arrayd, int typlen; bool typbyval; char typalign; + uint8 typalignby; array_iter iter; ArrayMetaState *inp_extra; ArrayMetaState *ret_extra; @@ -3270,21 +3275,21 @@ array_map(Datum arrayd, typlen = ret_extra->typlen; typbyval = ret_extra->typbyval; typalign = ret_extra->typalign; + typalignby = typalign_to_alignby(typalign); /* Allocate temporary arrays for new values */ values = (Datum *) palloc(nitems * sizeof(Datum)); nulls = (bool *) palloc(nitems * sizeof(bool)); /* Loop over source data */ - array_iter_setup(&iter, v); + array_iter_setup(&iter, v, inp_typlen, inp_typbyval, inp_typalign); hasnulls = false; for (i = 0; i < nitems; i++) { /* Get source element, checking for NULL */ *transform_source = - array_iter_next(&iter, transform_source_isnull, i, - inp_typlen, inp_typbyval, inp_typalign); + array_iter_next(&iter, transform_source_isnull, i); /* Apply the given expression to source element */ values[i] = ExecEvalExpr(exprstate, econtext, &nulls[i]); @@ -3298,7 +3303,7 @@ array_map(Datum arrayd, values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i])); /* Update total result size */ nbytes = att_addlength_datum(nbytes, typlen, values[i]); - nbytes = att_align_nominal(nbytes, typalign); + nbytes = att_nominal_alignby(nbytes, typalignby); /* check for overflow of total request */ if (!AllocSizeIsValid(nbytes)) ereport(ERROR, @@ -3505,6 +3510,7 @@ construct_md_array(Datum *elems, int32 dataoffset; int i; int nelems; + uint8 elmalignby = typalign_to_alignby(elmalign); if (ndims < 0) /* we do allow zero-dimension arrays */ ereport(ERROR, @@ -3538,7 +3544,7 @@ construct_md_array(Datum *elems, if (elmlen == -1) elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i])); nbytes = att_addlength_datum(nbytes, elmlen, elems[i]); - nbytes = att_align_nominal(nbytes, elmalign); + nbytes = att_nominal_alignby(nbytes, elmalignby); /* check for overflow of total request */ if (!AllocSizeIsValid(nbytes)) ereport(ERROR, @@ -3641,6 +3647,7 @@ deconstruct_array(const ArrayType *array, bits8 *bitmap; int bitmask; int i; + uint8 elmalignby = typalign_to_alignby(elmalign); Assert(ARR_ELEMTYPE(array) == elmtype); @@ -3673,7 +3680,7 @@ deconstruct_array(const ArrayType *array, { elems[i] = fetch_att(p, elmbyval, elmlen); p = att_addlength_pointer(p, elmlen, p); - p = (char *) att_align_nominal(p, elmalign); + p = (char *) att_nominal_alignby(p, elmalignby); } /* advance bitmap pointer if any */ @@ -3878,8 +3885,8 @@ array_eq(PG_FUNCTION_ARGS) /* Loop over source data */ nitems = ArrayGetNItems(ndims1, dims1); - array_iter_setup(&it1, array1); - array_iter_setup(&it2, array2); + array_iter_setup(&it1, array1, typlen, typbyval, typalign); + array_iter_setup(&it2, array2, typlen, typbyval, typalign); for (i = 0; i < nitems; i++) { @@ -3890,10 +3897,8 @@ array_eq(PG_FUNCTION_ARGS) bool oprresult; /* Get elements, checking for NULL */ - elt1 = array_iter_next(&it1, &isnull1, i, - typlen, typbyval, typalign); - elt2 = array_iter_next(&it2, &isnull2, i, - typlen, typbyval, typalign); + elt1 = array_iter_next(&it1, &isnull1, i); + elt2 = array_iter_next(&it2, &isnull2, i); /* * We consider two NULLs equal; NULL and not-NULL are unequal. @@ -4042,8 +4047,8 @@ array_cmp(FunctionCallInfo fcinfo) /* Loop over source data */ min_nitems = Min(nitems1, nitems2); - array_iter_setup(&it1, array1); - array_iter_setup(&it2, array2); + array_iter_setup(&it1, array1, typlen, typbyval, typalign); + array_iter_setup(&it2, array2, typlen, typbyval, typalign); for (i = 0; i < min_nitems; i++) { @@ -4054,8 +4059,8 @@ array_cmp(FunctionCallInfo fcinfo) int32 cmpresult; /* Get elements, checking for NULL */ - elt1 = array_iter_next(&it1, &isnull1, i, typlen, typbyval, typalign); - elt2 = array_iter_next(&it2, &isnull2, i, typlen, typbyval, typalign); + elt1 = array_iter_next(&it1, &isnull1, i); + elt2 = array_iter_next(&it2, &isnull2, i); /* * We consider two NULLs equal; NULL > not-NULL. @@ -4238,7 +4243,7 @@ hash_array(PG_FUNCTION_ARGS) /* Loop over source data */ nitems = ArrayGetNItems(ndims, dims); - array_iter_setup(&iter, array); + array_iter_setup(&iter, array, typlen, typbyval, typalign); for (i = 0; i < nitems; i++) { @@ -4247,7 +4252,7 @@ hash_array(PG_FUNCTION_ARGS) uint32 elthash; /* Get element, checking for NULL */ - elt = array_iter_next(&iter, &isnull, i, typlen, typbyval, typalign); + elt = array_iter_next(&iter, &isnull, i); if (isnull) { @@ -4328,7 +4333,7 @@ hash_array_extended(PG_FUNCTION_ARGS) /* Loop over source data */ nitems = ArrayGetNItems(ndims, dims); - array_iter_setup(&iter, array); + array_iter_setup(&iter, array, typlen, typbyval, typalign); for (i = 0; i < nitems; i++) { @@ -4337,7 +4342,7 @@ hash_array_extended(PG_FUNCTION_ARGS) uint64 elthash; /* Get element, checking for NULL */ - elt = array_iter_next(&iter, &isnull, i, typlen, typbyval, typalign); + elt = array_iter_next(&iter, &isnull, i); if (isnull) { @@ -4451,7 +4456,7 @@ array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation, /* Loop over source data */ nelems1 = ArrayGetNItems(AARR_NDIM(array1), AARR_DIMS(array1)); - array_iter_setup(&it1, array1); + array_iter_setup(&it1, array1, typlen, typbyval, typalign); for (i = 0; i < nelems1; i++) { @@ -4459,7 +4464,7 @@ array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation, bool isnull1; /* Get element, checking for NULL */ - elt1 = array_iter_next(&it1, &isnull1, i, typlen, typbyval, typalign); + elt1 = array_iter_next(&it1, &isnull1, i); /* * We assume that the comparison operator is strict, so a NULL can't @@ -4626,6 +4631,7 @@ array_create_iterator(ArrayType *arr, int slice_ndim, ArrayMetaState *mstate) &iterator->typlen, &iterator->typbyval, &iterator->typalign); + iterator->typalignby = typalign_to_alignby(iterator->typalign); /* * Remember the slicing parameters. @@ -4700,7 +4706,7 @@ array_iterate(ArrayIterator iterator, Datum *value, bool *isnull) /* Move our data pointer forward to the next element */ p = att_addlength_pointer(p, iterator->typlen, p); - p = (char *) att_align_nominal(p, iterator->typalign); + p = (char *) att_nominal_alignby(p, iterator->typalignby); iterator->data_ptr = p; } } @@ -4730,7 +4736,7 @@ array_iterate(ArrayIterator iterator, Datum *value, bool *isnull) /* Move our data pointer forward to the next element */ p = att_addlength_pointer(p, iterator->typlen, p); - p = (char *) att_align_nominal(p, iterator->typalign); + p = (char *) att_nominal_alignby(p, iterator->typalignby); } } @@ -4828,7 +4834,7 @@ static int ArrayCastAndSet(Datum src, int typlen, bool typbyval, - char typalign, + uint8 typalignby, char *dest) { int inc; @@ -4839,14 +4845,14 @@ ArrayCastAndSet(Datum src, store_att_byval(dest, src, typlen); else memmove(dest, DatumGetPointer(src), typlen); - inc = att_align_nominal(typlen, typalign); + inc = att_nominal_alignby(typlen, typalignby); } else { Assert(!typbyval); inc = att_addlength_datum(0, typlen, src); memmove(dest, DatumGetPointer(src), inc); - inc = att_align_nominal(inc, typalign); + inc = att_nominal_alignby(inc, typalignby); } return inc; @@ -4867,12 +4873,13 @@ static char * array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems, int typlen, bool typbyval, char typalign) { + uint8 typalignby = typalign_to_alignby(typalign); int bitmask; int i; /* easy if fixed-size elements and no NULLs */ if (typlen > 0 && !nullbitmap) - return ptr + nitems * ((Size) att_align_nominal(typlen, typalign)); + return ptr + nitems * ((Size) att_nominal_alignby(typlen, typalignby)); /* seems worth having separate loops for NULL and no-NULLs cases */ if (nullbitmap) @@ -4885,7 +4892,7 @@ array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems, if (*nullbitmap & bitmask) { ptr = att_addlength_pointer(ptr, typlen, ptr); - ptr = (char *) att_align_nominal(ptr, typalign); + ptr = (char *) att_nominal_alignby(ptr, typalignby); } bitmask <<= 1; if (bitmask == 0x100) @@ -4900,7 +4907,7 @@ array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems, for (i = 0; i < nitems; i++) { ptr = att_addlength_pointer(ptr, typlen, ptr); - ptr = (char *) att_align_nominal(ptr, typalign); + ptr = (char *) att_nominal_alignby(ptr, typalignby); } } return ptr; @@ -5050,12 +5057,13 @@ array_slice_size(char *arraydataptr, bits8 *arraynullsptr, j, inc; int count = 0; + uint8 typalignby = typalign_to_alignby(typalign); mda_get_range(ndim, span, st, endp); /* Pretty easy for fixed element length without nulls ... */ if (typlen > 0 && !arraynullsptr) - return ArrayGetNItems(ndim, span) * att_align_nominal(typlen, typalign); + return ArrayGetNItems(ndim, span) * att_nominal_alignby(typlen, typalignby); /* Else gotta do it the hard way */ src_offset = ArrayGetOffset(ndim, dim, lb, st); @@ -5077,7 +5085,7 @@ array_slice_size(char *arraydataptr, bits8 *arraynullsptr, if (!array_get_isnull(arraynullsptr, src_offset)) { inc = att_addlength_pointer(0, typlen, ptr); - inc = att_align_nominal(inc, typalign); + inc = att_nominal_alignby(inc, typalignby); ptr += inc; count += inc; } @@ -6096,6 +6104,7 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs, int16 elmlen; bool elmbyval; char elmalign; + uint8 elmalignby; ArrayMetaState *my_extra; /* @@ -6190,6 +6199,7 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs, elmlen = my_extra->typlen; elmbyval = my_extra->typbyval; elmalign = my_extra->typalign; + elmalignby = typalign_to_alignby(elmalign); /* compute required space */ if (!isnull) @@ -6204,7 +6214,7 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs, value = PointerGetDatum(PG_DETOAST_DATUM(value)); nbytes = att_addlength_datum(0, elmlen, value); - nbytes = att_align_nominal(nbytes, elmalign); + nbytes = att_nominal_alignby(nbytes, elmalignby); Assert(nbytes > 0); totbytes = nbytes * nitems; @@ -6228,7 +6238,7 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs, p = ARR_DATA_PTR(result); for (i = 0; i < nitems; i++) - p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p); + p += ArrayCastAndSet(value, elmlen, elmbyval, elmalignby, p); } else { @@ -6259,9 +6269,6 @@ array_unnest(PG_FUNCTION_ARGS) array_iter iter; int nextelem; int numelems; - int16 elmlen; - bool elmbyval; - char elmalign; } array_unnest_fctx; FuncCallContext *funcctx; @@ -6272,6 +6279,9 @@ array_unnest(PG_FUNCTION_ARGS) if (SRF_IS_FIRSTCALL()) { AnyArrayType *arr; + int16 elmlen; + bool elmbyval; + char elmalign; /* create a function context for cross-call persistence */ funcctx = SRF_FIRSTCALL_INIT(); @@ -6293,23 +6303,24 @@ array_unnest(PG_FUNCTION_ARGS) /* allocate memory for user context */ fctx = palloc_object(array_unnest_fctx); - /* initialize state */ - array_iter_setup(&fctx->iter, arr); - fctx->nextelem = 0; - fctx->numelems = ArrayGetNItems(AARR_NDIM(arr), AARR_DIMS(arr)); - + /* get element-type data */ if (VARATT_IS_EXPANDED_HEADER(arr)) { /* we can just grab the type data from expanded array */ - fctx->elmlen = arr->xpn.typlen; - fctx->elmbyval = arr->xpn.typbyval; - fctx->elmalign = arr->xpn.typalign; + elmlen = arr->xpn.typlen; + elmbyval = arr->xpn.typbyval; + elmalign = arr->xpn.typalign; } else get_typlenbyvalalign(AARR_ELEMTYPE(arr), - &fctx->elmlen, - &fctx->elmbyval, - &fctx->elmalign); + &elmlen, + &elmbyval, + &elmalign); + + /* initialize state */ + array_iter_setup(&fctx->iter, arr, elmlen, elmbyval, elmalign); + fctx->nextelem = 0; + fctx->numelems = ArrayGetNItems(AARR_NDIM(arr), AARR_DIMS(arr)); funcctx->user_fctx = fctx; MemoryContextSwitchTo(oldcontext); @@ -6324,8 +6335,7 @@ array_unnest(PG_FUNCTION_ARGS) int offset = fctx->nextelem++; Datum elem; - elem = array_iter_next(&fctx->iter, &fcinfo->isnull, offset, - fctx->elmlen, fctx->elmbyval, fctx->elmalign); + elem = array_iter_next(&fctx->iter, &fcinfo->isnull, offset); SRF_RETURN_NEXT(funcctx, elem); } @@ -6401,6 +6411,7 @@ array_replace_internal(ArrayType *array, int typlen; bool typbyval; char typalign; + uint8 typalignby; char *arraydataptr; bits8 *bitmap; int bitmask; @@ -6445,6 +6456,7 @@ array_replace_internal(ArrayType *array, typlen = typentry->typlen; typbyval = typentry->typbyval; typalign = typentry->typalign; + typalignby = typalign_to_alignby(typalign); /* * Detoast values if they are toasted. The replacement value must be @@ -6506,7 +6518,7 @@ array_replace_internal(ArrayType *array, isNull = false; elt = fetch_att(arraydataptr, typbyval, typlen); arraydataptr = att_addlength_datum(arraydataptr, typlen, elt); - arraydataptr = (char *) att_align_nominal(arraydataptr, typalign); + arraydataptr = (char *) att_nominal_alignby(arraydataptr, typalignby); if (search_isnull) { @@ -6553,7 +6565,7 @@ array_replace_internal(ArrayType *array, { /* Update total result size */ nbytes = att_addlength_datum(nbytes, typlen, values[nresult]); - nbytes = att_align_nominal(nbytes, typalign); + nbytes = att_nominal_alignby(nbytes, typalignby); /* check for overflow of total request */ if (!AllocSizeIsValid(nbytes)) ereport(ERROR, @@ -6860,6 +6872,7 @@ width_bucket_array_variable(Datum operand, int typlen = typentry->typlen; bool typbyval = typentry->typbyval; char typalign = typentry->typalign; + uint8 typalignby = typalign_to_alignby(typalign); int left; int right; @@ -6883,7 +6896,7 @@ width_bucket_array_variable(Datum operand, for (i = left; i < mid; i++) { ptr = att_addlength_pointer(ptr, typlen, ptr); - ptr = (char *) att_align_nominal(ptr, typalign); + ptr = (char *) att_nominal_alignby(ptr, typalignby); } locfcinfo->args[0].value = operand; @@ -6908,7 +6921,7 @@ width_bucket_array_variable(Datum operand, * ensures we do only O(N) array indexing work, not O(N^2). */ ptr = att_addlength_pointer(ptr, typlen, ptr); - thresholds_data = (char *) att_align_nominal(ptr, typalign); + thresholds_data = (char *) att_nominal_alignby(ptr, typalignby); } } diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c index 07e2a81d46a..b1942387dc5 100644 --- a/src/backend/utils/adt/multirangetypes.c +++ b/src/backend/utils/adt/multirangetypes.c @@ -572,21 +572,22 @@ multirange_size_estimate(TypeCacheEntry *rangetyp, int32 range_count, RangeType **ranges) { char elemalign = rangetyp->rngelemtype->typalign; + uint8 elemalignby = typalign_to_alignby(elemalign); Size size; int32 i; /* * Count space for MultirangeType struct, items and flags. */ - size = att_align_nominal(sizeof(MultirangeType) + - Max(range_count - 1, 0) * sizeof(uint32) + - range_count * sizeof(uint8), elemalign); + size = att_nominal_alignby(sizeof(MultirangeType) + + Max(range_count - 1, 0) * sizeof(uint32) + + range_count * sizeof(uint8), elemalignby); /* Count space for range bounds */ for (i = 0; i < range_count; i++) - size += att_align_nominal(VARSIZE(ranges[i]) - - sizeof(RangeType) - - sizeof(char), elemalign); + size += att_nominal_alignby(VARSIZE(ranges[i]) - + sizeof(RangeType) - + sizeof(char), elemalignby); return size; } @@ -605,6 +606,7 @@ write_multirange_data(MultirangeType *multirange, TypeCacheEntry *rangetyp, const char *begin; char *ptr; char elemalign = rangetyp->rngelemtype->typalign; + uint8 elemalignby = typalign_to_alignby(elemalign); items = MultirangeGetItemsPtr(multirange); flags = MultirangeGetFlagsPtr(multirange); @@ -630,7 +632,7 @@ write_multirange_data(MultirangeType *multirange, TypeCacheEntry *rangetyp, flags[i] = *((char *) ranges[i] + VARSIZE(ranges[i]) - sizeof(char)); len = VARSIZE(ranges[i]) - sizeof(RangeType) - sizeof(char); memcpy(ptr, ranges[i] + 1, len); - ptr += att_align_nominal(len, elemalign); + ptr += att_nominal_alignby(len, elemalignby); } } diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c index 6c1ebb0866d..552ac0c61d3 100644 --- a/src/backend/utils/adt/varlena.c +++ b/src/backend/utils/adt/varlena.c @@ -3898,6 +3898,7 @@ array_to_text_internal(FunctionCallInfo fcinfo, ArrayType *v, int typlen; bool typbyval; char typalign; + uint8 typalignby; StringInfoData buf; bool printed = false; char *p; @@ -3947,6 +3948,7 @@ array_to_text_internal(FunctionCallInfo fcinfo, ArrayType *v, typlen = my_extra->typlen; typbyval = my_extra->typbyval; typalign = my_extra->typalign; + typalignby = typalign_to_alignby(typalign); p = ARR_DATA_PTR(v); bitmap = ARR_NULLBITMAP(v); @@ -3983,7 +3985,7 @@ array_to_text_internal(FunctionCallInfo fcinfo, ArrayType *v, printed = true; p = att_addlength_pointer(p, typlen, p); - p = (char *) att_align_nominal(p, typalign); + p = (char *) att_nominal_alignby(p, typalignby); } /* advance bitmap pointer if any */ diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h index 3e5530658c9..d64c18b950b 100644 --- a/src/include/access/tupmacs.h +++ b/src/include/access/tupmacs.h @@ -71,6 +71,43 @@ fetch_att(const void *T, bool attbyval, int attlen) } #endif /* FRONTEND */ +/* + * typalign_to_alignby: map a TYPALIGN_xxx value to the numeric alignment + * value it represents. (We store TYPALIGN_xxx codes not the real alignment + * values mainly so that initial catalog contents can be machine-independent.) + */ +static inline uint8 +typalign_to_alignby(char typalign) +{ + uint8 alignby; + + switch (typalign) + { + case TYPALIGN_CHAR: + alignby = sizeof(char); + break; + case TYPALIGN_SHORT: + alignby = ALIGNOF_SHORT; + break; + case TYPALIGN_INT: + alignby = ALIGNOF_INT; + break; + case TYPALIGN_DOUBLE: + alignby = ALIGNOF_DOUBLE; + break; + default: +#ifndef FRONTEND + elog(ERROR, "invalid typalign value: %c", typalign); +#else + fprintf(stderr, "invalid typalign value: %c\n", typalign); + exit(1); +#endif + alignby = 0; + break; + } + return alignby; +} + /* * att_align_datum aligns the given offset as needed for a datum of alignment * requirement attalign and typlen attlen. attdatum is the Datum variable @@ -139,19 +176,11 @@ fetch_att(const void *T, bool attbyval, int attlen) * * within arrays and multiranges, we unconditionally align varlenas (XXX this * should be revisited, probably). * - * The attalign cases are tested in what is hopefully something like their - * frequency of occurrence. + * In performance-critical loops, avoid using this macro; instead use + * att_nominal_alignby with a pre-computed alignby value. */ #define att_align_nominal(cur_offset, attalign) \ -( \ - ((attalign) == TYPALIGN_INT) ? INTALIGN(cur_offset) : \ - (((attalign) == TYPALIGN_CHAR) ? (uintptr_t) (cur_offset) : \ - (((attalign) == TYPALIGN_DOUBLE) ? DOUBLEALIGN(cur_offset) : \ - ( \ - AssertMacro((attalign) == TYPALIGN_SHORT), \ - SHORTALIGN(cur_offset) \ - ))) \ -) + att_nominal_alignby(cur_offset, typalign_to_alignby(attalign)) /* * Similar to att_align_nominal, but accepts a number of bytes, typically from diff --git a/src/include/utils/arrayaccess.h b/src/include/utils/arrayaccess.h index abb8659de02..a325ae52574 100644 --- a/src/include/utils/arrayaccess.h +++ b/src/include/utils/arrayaccess.h @@ -22,8 +22,8 @@ * Functions for iterating through elements of a flat or expanded array. * These require a state struct "array_iter iter". * - * Use "array_iter_setup(&iter, arrayptr);" to prepare to iterate, and - * "datumvar = array_iter_next(&iter, &isnullvar, index, ...);" to fetch + * Use "array_iter_setup(&iter, arrayptr, ...);" to prepare to iterate, + * and "datumvar = array_iter_next(&iter, &isnullvar, index);" to fetch * the next element into datumvar/isnullvar. * "index" must be the zero-origin element number; we make caller provide * this since caller is generally counting the elements anyway. Despite @@ -42,11 +42,17 @@ typedef struct array_iter char *dataptr; /* Current spot in the data area */ bits8 *bitmapptr; /* Current byte of the nulls bitmap, or NULL */ int bitmask; /* mask for current bit in nulls bitmap */ + + /* Fields used in both cases: data about array's element type */ + int elmlen; + bool elmbyval; + uint8 elmalignby; } array_iter; static inline void -array_iter_setup(array_iter *it, AnyArrayType *a) +array_iter_setup(array_iter *it, AnyArrayType *a, + int elmlen, bool elmbyval, char elmalign) { if (VARATT_IS_EXPANDED_HEADER(a)) { @@ -75,11 +81,13 @@ array_iter_setup(array_iter *it, AnyArrayType *a) it->bitmapptr = ARR_NULLBITMAP((ArrayType *) a); } it->bitmask = 1; + it->elmlen = elmlen; + it->elmbyval = elmbyval; + it->elmalignby = typalign_to_alignby(elmalign); } static inline Datum -array_iter_next(array_iter *it, bool *isnull, int i, - int elmlen, bool elmbyval, char elmalign) +array_iter_next(array_iter *it, bool *isnull, int i) { Datum ret; @@ -98,10 +106,11 @@ array_iter_next(array_iter *it, bool *isnull, int i, else { *isnull = false; - ret = fetch_att(it->dataptr, elmbyval, elmlen); - it->dataptr = att_addlength_pointer(it->dataptr, elmlen, + ret = fetch_att(it->dataptr, it->elmbyval, it->elmlen); + it->dataptr = att_addlength_pointer(it->dataptr, it->elmlen, it->dataptr); - it->dataptr = (char *) att_align_nominal(it->dataptr, elmalign); + it->dataptr = (char *) att_nominal_alignby(it->dataptr, + it->elmalignby); } it->bitmask <<= 1; if (it->bitmask == 0x100) diff --git a/src/pl/plpython/plpy_typeio.c b/src/pl/plpython/plpy_typeio.c index 1f69109b081..44055de6aeb 100644 --- a/src/pl/plpython/plpy_typeio.c +++ b/src/pl/plpython/plpy_typeio.c @@ -735,6 +735,7 @@ PLyList_FromArray_recurse(PLyDatumToOb *elm, int *dims, int ndim, int dim, char *dataptr = *dataptr_p; bits8 *bitmap = *bitmap_p; int bitmask = *bitmask_p; + uint8 typalignby = typalign_to_alignby(elm->typalign); for (i = 0; i < dims[dim]; i++) { @@ -751,7 +752,7 @@ PLyList_FromArray_recurse(PLyDatumToOb *elm, int *dims, int ndim, int dim, itemvalue = fetch_att(dataptr, elm->typbyval, elm->typlen); PyList_SetItem(list, i, elm->func(elm, itemvalue)); dataptr = att_addlength_pointer(dataptr, elm->typlen, dataptr); - dataptr = (char *) att_align_nominal(dataptr, elm->typalign); + dataptr = (char *) att_nominal_alignby(dataptr, typalignby); } /* advance bitmap pointer if any */ -- 2.47.3