diff options
author | David Rowley <drowley@postgresql.org> | 2021-07-06 12:24:43 +1200 |
---|---|---|
committer | David Rowley <drowley@postgresql.org> | 2021-07-06 12:24:43 +1200 |
commit | 53d86957e980efa06f15232b8cff430d4cc6dd64 (patch) | |
tree | db322c36039feebc96d95e4324600ea2df9ae6cc | |
parent | 2aca19f2989aa938ece7306678f5494a984ece3f (diff) | |
download | postgresql-53d86957e980efa06f15232b8cff430d4cc6dd64.tar.gz postgresql-53d86957e980efa06f15232b8cff430d4cc6dd64.zip |
Reduce the number of pallocs when building partition bounds
In each of the create_*_bound() functions for LIST, RANGE and HASH
partitioning, there were a large number of palloc calls which could be
reduced down to a much smaller number.
In each of these functions, an array was built so that we could qsort it
before making the PartitionBoundInfo. For LIST and HASH partitioning, an
array of pointers was allocated then each element was allocated within
that array. Since the number of items of each dimension is known
beforehand, we can just allocate a single chunk of memory for this.
Similarly, with all partition strategies, we're able to reduce the number
of allocations to build the ->datums field. This is an array of Datum
pointers, but there's no need for the Datums that each element points to
to be singly allocated. One big chunk will do. For RANGE partitioning,
the PartitionBoundInfo->kind field can get the same treatment.
We can apply the same optimizations to partition_bounds_copy(). Doing
this might have a small effect on cache performance when searching for the
correct partition during partition pruning or DML on a partitioned table.
However, that's likely to be small and this is mostly about reducing
palloc overhead.
Author: Nitin Jadhav, Justin Pryzby, David Rowley
Reviewed-by: Justin Pryzby, Zhihong Yu
Discussion: https://postgr.es/m/flat/CAMm1aWYFTqEio3bURzZh47jveiHRwgQTiSDvBORczNEz2duZ1Q@mail.gmail.com
-rw-r--r-- | src/backend/partitioning/partbounds.c | 194 |
1 files changed, 123 insertions, 71 deletions
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c index 7096d3bf450..00c394445a8 100644 --- a/src/backend/partitioning/partbounds.c +++ b/src/backend/partitioning/partbounds.c @@ -358,10 +358,10 @@ create_hash_bounds(PartitionBoundSpec **boundspecs, int nparts, PartitionKey key, int **mapping) { PartitionBoundInfo boundinfo; - PartitionHashBound **hbounds = NULL; + PartitionHashBound *hbounds; int i; - int ndatums = 0; int greatest_modulus; + Datum *boundDatums; boundinfo = (PartitionBoundInfoData *) palloc0(sizeof(PartitionBoundInfoData)); @@ -370,9 +370,8 @@ create_hash_bounds(PartitionBoundSpec **boundspecs, int nparts, boundinfo->null_index = -1; boundinfo->default_index = -1; - ndatums = nparts; - hbounds = (PartitionHashBound **) - palloc(nparts * sizeof(PartitionHashBound *)); + hbounds = (PartitionHashBound *) + palloc(nparts * sizeof(PartitionHashBound)); /* Convert from node to the internal representation */ for (i = 0; i < nparts; i++) @@ -382,37 +381,44 @@ create_hash_bounds(PartitionBoundSpec **boundspecs, int nparts, if (spec->strategy != PARTITION_STRATEGY_HASH) elog(ERROR, "invalid strategy in partition bound spec"); - hbounds[i] = (PartitionHashBound *) palloc(sizeof(PartitionHashBound)); - hbounds[i]->modulus = spec->modulus; - hbounds[i]->remainder = spec->remainder; - hbounds[i]->index = i; + hbounds[i].modulus = spec->modulus; + hbounds[i].remainder = spec->remainder; + hbounds[i].index = i; } /* Sort all the bounds in ascending order */ - qsort(hbounds, nparts, sizeof(PartitionHashBound *), + qsort(hbounds, nparts, sizeof(PartitionHashBound), qsort_partition_hbound_cmp); /* After sorting, moduli are now stored in ascending order. */ - greatest_modulus = hbounds[ndatums - 1]->modulus; + greatest_modulus = hbounds[nparts - 1].modulus; - boundinfo->ndatums = ndatums; - boundinfo->datums = (Datum **) palloc0(ndatums * sizeof(Datum *)); + boundinfo->ndatums = nparts; + boundinfo->datums = (Datum **) palloc0(nparts * sizeof(Datum *)); + boundinfo->kind = NULL; boundinfo->nindexes = greatest_modulus; boundinfo->indexes = (int *) palloc(greatest_modulus * sizeof(int)); for (i = 0; i < greatest_modulus; i++) boundinfo->indexes[i] = -1; /* + * In the loop below, to save from allocating a series of small datum + * arrays, here we just allocate a single array and below we'll just + * assign a portion of this array per partition. + */ + boundDatums = (Datum *) palloc(nparts * 2 * sizeof(Datum)); + + /* * For hash partitioning, there are as many datums (modulus and remainder * pairs) as there are partitions. Indexes are simply values ranging from * 0 to (nparts - 1). */ for (i = 0; i < nparts; i++) { - int modulus = hbounds[i]->modulus; - int remainder = hbounds[i]->remainder; + int modulus = hbounds[i].modulus; + int remainder = hbounds[i].remainder; - boundinfo->datums[i] = (Datum *) palloc(2 * sizeof(Datum)); + boundinfo->datums[i] = &boundDatums[i * 2]; boundinfo->datums[i][0] = Int32GetDatum(modulus); boundinfo->datums[i][1] = Int32GetDatum(remainder); @@ -424,8 +430,7 @@ create_hash_bounds(PartitionBoundSpec **boundspecs, int nparts, remainder += modulus; } - (*mapping)[hbounds[i]->index] = i; - pfree(hbounds[i]); + (*mapping)[hbounds[i].index] = i; } pfree(hbounds); @@ -433,6 +438,32 @@ create_hash_bounds(PartitionBoundSpec **boundspecs, int nparts, } /* + * get_non_null_list_datum_count + * Counts the number of non-null Datums in each partition. + */ +static int +get_non_null_list_datum_count(PartitionBoundSpec **boundspecs, int nparts) +{ + int i; + int count = 0; + + for (i = 0; i < nparts; i++) + { + ListCell *lc; + + foreach(lc, boundspecs[i]->listdatums) + { + Const *val = castNode(Const, lfirst(lc)); + + if (!val->constisnull) + count++; + } + } + + return count; +} + +/* * create_list_bounds * Create a PartitionBoundInfo for a list partitioned table */ @@ -441,14 +472,14 @@ create_list_bounds(PartitionBoundSpec **boundspecs, int nparts, PartitionKey key, int **mapping) { PartitionBoundInfo boundinfo; - PartitionListValue **all_values = NULL; - ListCell *cell; - int i = 0; - int ndatums = 0; + PartitionListValue *all_values; + int i; + int j; + int ndatums; int next_index = 0; int default_index = -1; int null_index = -1; - List *non_null_values = NIL; + Datum *boundDatums; boundinfo = (PartitionBoundInfoData *) palloc0(sizeof(PartitionBoundInfoData)); @@ -457,8 +488,12 @@ create_list_bounds(PartitionBoundSpec **boundspecs, int nparts, boundinfo->null_index = -1; boundinfo->default_index = -1; + ndatums = get_non_null_list_datum_count(boundspecs, nparts); + all_values = (PartitionListValue *) + palloc(ndatums * sizeof(PartitionListValue)); + /* Create a unified list of non-null values across all partitions. */ - for (i = 0; i < nparts; i++) + for (j = 0, i = 0; i < nparts; i++) { PartitionBoundSpec *spec = boundspecs[i]; ListCell *c; @@ -480,14 +515,12 @@ create_list_bounds(PartitionBoundSpec **boundspecs, int nparts, foreach(c, spec->listdatums) { Const *val = castNode(Const, lfirst(c)); - PartitionListValue *list_value = NULL; if (!val->constisnull) { - list_value = (PartitionListValue *) - palloc0(sizeof(PartitionListValue)); - list_value->index = i; - list_value->value = val->constvalue; + all_values[j].index = i; + all_values[j].value = val->constvalue; + j++; } else { @@ -499,41 +532,29 @@ create_list_bounds(PartitionBoundSpec **boundspecs, int nparts, elog(ERROR, "found null more than once"); null_index = i; } - - if (list_value) - non_null_values = lappend(non_null_values, list_value); } } - ndatums = list_length(non_null_values); - - /* - * Collect all list values in one array. Alongside the value, we also save - * the index of partition the value comes from. - */ - all_values = (PartitionListValue **) - palloc(ndatums * sizeof(PartitionListValue *)); - i = 0; - foreach(cell, non_null_values) - { - PartitionListValue *src = lfirst(cell); - - all_values[i] = (PartitionListValue *) - palloc(sizeof(PartitionListValue)); - all_values[i]->value = src->value; - all_values[i]->index = src->index; - i++; - } + /* ensure we found a Datum for every slot in the all_values array */ + Assert(j == ndatums); - qsort_arg(all_values, ndatums, sizeof(PartitionListValue *), + qsort_arg(all_values, ndatums, sizeof(PartitionListValue), qsort_partition_list_value_cmp, (void *) key); boundinfo->ndatums = ndatums; boundinfo->datums = (Datum **) palloc0(ndatums * sizeof(Datum *)); + boundinfo->kind = NULL; boundinfo->nindexes = ndatums; boundinfo->indexes = (int *) palloc(ndatums * sizeof(int)); /* + * In the loop below, to save from allocating a series of small datum + * arrays, here we just allocate a single array and below we'll just + * assign a portion of this array per datum. + */ + boundDatums = (Datum *) palloc(ndatums * sizeof(Datum)); + + /* * Copy values. Canonical indexes are values ranging from 0 to (nparts - * 1) assigned to each partition such that all datums of a given partition * receive the same value. The value for a given partition is the index of @@ -541,10 +562,10 @@ create_list_bounds(PartitionBoundSpec **boundspecs, int nparts, */ for (i = 0; i < ndatums; i++) { - int orig_index = all_values[i]->index; + int orig_index = all_values[i].index; - boundinfo->datums[i] = (Datum *) palloc(sizeof(Datum)); - boundinfo->datums[i][0] = datumCopy(all_values[i]->value, + boundinfo->datums[i] = &boundDatums[i]; + boundinfo->datums[i][0] = datumCopy(all_values[i].value, key->parttypbyval[0], key->parttyplen[0]); @@ -555,6 +576,8 @@ create_list_bounds(PartitionBoundSpec **boundspecs, int nparts, boundinfo->indexes[i] = (*mapping)[orig_index]; } + pfree(all_values); + /* * Set the canonical value for null_index, if any. * @@ -603,10 +626,13 @@ create_range_bounds(PartitionBoundSpec **boundspecs, int nparts, PartitionRangeBound **all_bounds, *prev; int i, - k; + k, + partnatts; int ndatums = 0; int default_index = -1; int next_index = 0; + Datum *boundDatums; + PartitionRangeDatumKind *boundKinds; boundinfo = (PartitionBoundInfoData *) palloc0(sizeof(PartitionBoundInfoData)); @@ -707,6 +733,8 @@ create_range_bounds(PartitionBoundSpec **boundspecs, int nparts, prev = cur; } + pfree(all_bounds); + /* Update ndatums to hold the count of distinct datums. */ ndatums = k; @@ -731,16 +759,24 @@ create_range_bounds(PartitionBoundSpec **boundspecs, int nparts, boundinfo->nindexes = ndatums + 1; boundinfo->indexes = (int *) palloc((ndatums + 1) * sizeof(int)); + /* + * In the loop below, to save from allocating a series of small arrays, + * here we just allocate a single array for Datums and another for + * PartitionRangeDatumKinds, below we'll just assign a portion of these + * arrays in each loop. + */ + partnatts = key->partnatts; + boundDatums = (Datum *) palloc(ndatums * partnatts * sizeof(Datum)); + boundKinds = (PartitionRangeDatumKind *) palloc(ndatums * partnatts * + sizeof(PartitionRangeDatumKind)); + for (i = 0; i < ndatums; i++) { int j; - boundinfo->datums[i] = (Datum *) palloc(key->partnatts * - sizeof(Datum)); - boundinfo->kind[i] = (PartitionRangeDatumKind *) - palloc(key->partnatts * - sizeof(PartitionRangeDatumKind)); - for (j = 0; j < key->partnatts; j++) + boundinfo->datums[i] = &boundDatums[i * partnatts]; + boundinfo->kind[i] = &boundKinds[i * partnatts]; + for (j = 0; j < partnatts; j++) { if (rbounds[i]->kind[j] == PARTITION_RANGE_DATUM_VALUE) boundinfo->datums[i][j] = @@ -772,6 +808,8 @@ create_range_bounds(PartitionBoundSpec **boundspecs, int nparts, } } + pfree(rbounds); + /* Set the canonical value for default_index, if any. */ if (default_index != -1) { @@ -914,6 +952,7 @@ partition_bounds_copy(PartitionBoundInfo src, int partnatts; bool hash_part; int natts; + Datum *boundDatums; dest = (PartitionBoundInfo) palloc(sizeof(PartitionBoundInfoData)); @@ -929,15 +968,27 @@ partition_bounds_copy(PartitionBoundInfo src, if (src->kind != NULL) { + PartitionRangeDatumKind *boundKinds; + + /* only RANGE partition should have a non-NULL kind */ + Assert(key->strategy == PARTITION_STRATEGY_RANGE); + dest->kind = (PartitionRangeDatumKind **) palloc(ndatums * sizeof(PartitionRangeDatumKind *)); + + /* + * In the loop below, to save from allocating a series of small arrays + * for storing the PartitionRangeDatumKind, we allocate a single chunk + * here and use a smaller portion of it for each datum. + */ + boundKinds = (PartitionRangeDatumKind *) palloc(ndatums * partnatts * + sizeof(PartitionRangeDatumKind)); + for (i = 0; i < ndatums; i++) { - dest->kind[i] = (PartitionRangeDatumKind *) palloc(partnatts * - sizeof(PartitionRangeDatumKind)); - + dest->kind[i] = &boundKinds[i * partnatts]; memcpy(dest->kind[i], src->kind[i], - sizeof(PartitionRangeDatumKind) * key->partnatts); + sizeof(PartitionRangeDatumKind) * partnatts); } } else @@ -949,12 +1000,13 @@ partition_bounds_copy(PartitionBoundInfo src, */ hash_part = (key->strategy == PARTITION_STRATEGY_HASH); natts = hash_part ? 2 : partnatts; + boundDatums = palloc(ndatums * natts * sizeof(Datum)); for (i = 0; i < ndatums; i++) { int j; - dest->datums[i] = (Datum *) palloc(sizeof(Datum) * natts); + dest->datums[i] = &boundDatums[i * natts]; for (j = 0; j < natts; j++) { @@ -3682,8 +3734,8 @@ partition_hash_bsearch(PartitionBoundInfo boundinfo, static int32 qsort_partition_hbound_cmp(const void *a, const void *b) { - PartitionHashBound *h1 = (*(PartitionHashBound *const *) a); - PartitionHashBound *h2 = (*(PartitionHashBound *const *) b); + PartitionHashBound *const h1 = (PartitionHashBound *const) a; + PartitionHashBound *const h2 = (PartitionHashBound *const) b; return partition_hbound_cmp(h1->modulus, h1->remainder, h2->modulus, h2->remainder); @@ -3697,8 +3749,8 @@ qsort_partition_hbound_cmp(const void *a, const void *b) static int32 qsort_partition_list_value_cmp(const void *a, const void *b, void *arg) { - Datum val1 = (*(PartitionListValue *const *) a)->value, - val2 = (*(PartitionListValue *const *) b)->value; + Datum val1 = ((PartitionListValue *const) a)->value, + val2 = ((PartitionListValue *const) b)->value; PartitionKey key = (PartitionKey) arg; return DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0], |