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
This commit is contained in:
David Rowley 2021-07-06 12:24:43 +12:00
parent 2aca19f298
commit 53d86957e9
1 changed files with 123 additions and 71 deletions

View File

@ -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,26 +381,33 @@ 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
@ -409,10 +415,10 @@ create_hash_bounds(PartitionBoundSpec **boundspecs, int nparts,
*/
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,14 +430,39 @@ create_hash_bounds(PartitionBoundSpec **boundspecs, int nparts,
remainder += modulus;
}
(*mapping)[hbounds[i]->index] = i;
pfree(hbounds[i]);
(*mapping)[hbounds[i].index] = i;
}
pfree(hbounds);
return boundinfo;
}
/*
* 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,40 +532,28 @@ 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);
/* ensure we found a Datum for every slot in the all_values array */
Assert(j == ndatums);
/*
* 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++;
}
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
@ -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],