aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Rowley <drowley@postgresql.org>2021-07-06 12:24:43 +1200
committerDavid Rowley <drowley@postgresql.org>2021-07-06 12:24:43 +1200
commit53d86957e980efa06f15232b8cff430d4cc6dd64 (patch)
treedb322c36039feebc96d95e4324600ea2df9ae6cc
parent2aca19f2989aa938ece7306678f5494a984ece3f (diff)
downloadpostgresql-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.c194
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],