Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 53d8695

Browse files
committed
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
1 parent 2aca19f commit 53d8695

File tree

1 file changed

+123
-71
lines changed

1 file changed

+123
-71
lines changed

src/backend/partitioning/partbounds.c

Lines changed: 123 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -358,10 +358,10 @@ create_hash_bounds(PartitionBoundSpec **boundspecs, int nparts,
358358
PartitionKey key, int **mapping)
359359
{
360360
PartitionBoundInfo boundinfo;
361-
PartitionHashBound **hbounds = NULL;
361+
PartitionHashBound *hbounds;
362362
int i;
363-
int ndatums = 0;
364363
int greatest_modulus;
364+
Datum *boundDatums;
365365

366366
boundinfo = (PartitionBoundInfoData *)
367367
palloc0(sizeof(PartitionBoundInfoData));
@@ -370,9 +370,8 @@ create_hash_bounds(PartitionBoundSpec **boundspecs, int nparts,
370370
boundinfo->null_index = -1;
371371
boundinfo->default_index = -1;
372372

373-
ndatums = nparts;
374-
hbounds = (PartitionHashBound **)
375-
palloc(nparts * sizeof(PartitionHashBound *));
373+
hbounds = (PartitionHashBound *)
374+
palloc(nparts * sizeof(PartitionHashBound));
376375

377376
/* Convert from node to the internal representation */
378377
for (i = 0; i < nparts; i++)
@@ -382,37 +381,44 @@ create_hash_bounds(PartitionBoundSpec **boundspecs, int nparts,
382381
if (spec->strategy != PARTITION_STRATEGY_HASH)
383382
elog(ERROR, "invalid strategy in partition bound spec");
384383

385-
hbounds[i] = (PartitionHashBound *) palloc(sizeof(PartitionHashBound));
386-
hbounds[i]->modulus = spec->modulus;
387-
hbounds[i]->remainder = spec->remainder;
388-
hbounds[i]->index = i;
384+
hbounds[i].modulus = spec->modulus;
385+
hbounds[i].remainder = spec->remainder;
386+
hbounds[i].index = i;
389387
}
390388

391389
/* Sort all the bounds in ascending order */
392-
qsort(hbounds, nparts, sizeof(PartitionHashBound *),
390+
qsort(hbounds, nparts, sizeof(PartitionHashBound),
393391
qsort_partition_hbound_cmp);
394392

395393
/* After sorting, moduli are now stored in ascending order. */
396-
greatest_modulus = hbounds[ndatums - 1]->modulus;
394+
greatest_modulus = hbounds[nparts - 1].modulus;
397395

398-
boundinfo->ndatums = ndatums;
399-
boundinfo->datums = (Datum **) palloc0(ndatums * sizeof(Datum *));
396+
boundinfo->ndatums = nparts;
397+
boundinfo->datums = (Datum **) palloc0(nparts * sizeof(Datum *));
398+
boundinfo->kind = NULL;
400399
boundinfo->nindexes = greatest_modulus;
401400
boundinfo->indexes = (int *) palloc(greatest_modulus * sizeof(int));
402401
for (i = 0; i < greatest_modulus; i++)
403402
boundinfo->indexes[i] = -1;
404403

404+
/*
405+
* In the loop below, to save from allocating a series of small datum
406+
* arrays, here we just allocate a single array and below we'll just
407+
* assign a portion of this array per partition.
408+
*/
409+
boundDatums = (Datum *) palloc(nparts * 2 * sizeof(Datum));
410+
405411
/*
406412
* For hash partitioning, there are as many datums (modulus and remainder
407413
* pairs) as there are partitions. Indexes are simply values ranging from
408414
* 0 to (nparts - 1).
409415
*/
410416
for (i = 0; i < nparts; i++)
411417
{
412-
int modulus = hbounds[i]->modulus;
413-
int remainder = hbounds[i]->remainder;
418+
int modulus = hbounds[i].modulus;
419+
int remainder = hbounds[i].remainder;
414420

415-
boundinfo->datums[i] = (Datum *) palloc(2 * sizeof(Datum));
421+
boundinfo->datums[i] = &boundDatums[i * 2];
416422
boundinfo->datums[i][0] = Int32GetDatum(modulus);
417423
boundinfo->datums[i][1] = Int32GetDatum(remainder);
418424

@@ -424,14 +430,39 @@ create_hash_bounds(PartitionBoundSpec **boundspecs, int nparts,
424430
remainder += modulus;
425431
}
426432

427-
(*mapping)[hbounds[i]->index] = i;
428-
pfree(hbounds[i]);
433+
(*mapping)[hbounds[i].index] = i;
429434
}
430435
pfree(hbounds);
431436

432437
return boundinfo;
433438
}
434439

440+
/*
441+
* get_non_null_list_datum_count
442+
* Counts the number of non-null Datums in each partition.
443+
*/
444+
static int
445+
get_non_null_list_datum_count(PartitionBoundSpec **boundspecs, int nparts)
446+
{
447+
int i;
448+
int count = 0;
449+
450+
for (i = 0; i < nparts; i++)
451+
{
452+
ListCell *lc;
453+
454+
foreach(lc, boundspecs[i]->listdatums)
455+
{
456+
Const *val = castNode(Const, lfirst(lc));
457+
458+
if (!val->constisnull)
459+
count++;
460+
}
461+
}
462+
463+
return count;
464+
}
465+
435466
/*
436467
* create_list_bounds
437468
* Create a PartitionBoundInfo for a list partitioned table
@@ -441,14 +472,14 @@ create_list_bounds(PartitionBoundSpec **boundspecs, int nparts,
441472
PartitionKey key, int **mapping)
442473
{
443474
PartitionBoundInfo boundinfo;
444-
PartitionListValue **all_values = NULL;
445-
ListCell *cell;
446-
int i = 0;
447-
int ndatums = 0;
475+
PartitionListValue *all_values;
476+
int i;
477+
int j;
478+
int ndatums;
448479
int next_index = 0;
449480
int default_index = -1;
450481
int null_index = -1;
451-
List *non_null_values = NIL;
482+
Datum *boundDatums;
452483

453484
boundinfo = (PartitionBoundInfoData *)
454485
palloc0(sizeof(PartitionBoundInfoData));
@@ -457,8 +488,12 @@ create_list_bounds(PartitionBoundSpec **boundspecs, int nparts,
457488
boundinfo->null_index = -1;
458489
boundinfo->default_index = -1;
459490

491+
ndatums = get_non_null_list_datum_count(boundspecs, nparts);
492+
all_values = (PartitionListValue *)
493+
palloc(ndatums * sizeof(PartitionListValue));
494+
460495
/* Create a unified list of non-null values across all partitions. */
461-
for (i = 0; i < nparts; i++)
496+
for (j = 0, i = 0; i < nparts; i++)
462497
{
463498
PartitionBoundSpec *spec = boundspecs[i];
464499
ListCell *c;
@@ -480,14 +515,12 @@ create_list_bounds(PartitionBoundSpec **boundspecs, int nparts,
480515
foreach(c, spec->listdatums)
481516
{
482517
Const *val = castNode(Const, lfirst(c));
483-
PartitionListValue *list_value = NULL;
484518

485519
if (!val->constisnull)
486520
{
487-
list_value = (PartitionListValue *)
488-
palloc0(sizeof(PartitionListValue));
489-
list_value->index = i;
490-
list_value->value = val->constvalue;
521+
all_values[j].index = i;
522+
all_values[j].value = val->constvalue;
523+
j++;
491524
}
492525
else
493526
{
@@ -499,40 +532,28 @@ create_list_bounds(PartitionBoundSpec **boundspecs, int nparts,
499532
elog(ERROR, "found null more than once");
500533
null_index = i;
501534
}
502-
503-
if (list_value)
504-
non_null_values = lappend(non_null_values, list_value);
505535
}
506536
}
507537

508-
ndatums = list_length(non_null_values);
509-
510-
/*
511-
* Collect all list values in one array. Alongside the value, we also save
512-
* the index of partition the value comes from.
513-
*/
514-
all_values = (PartitionListValue **)
515-
palloc(ndatums * sizeof(PartitionListValue *));
516-
i = 0;
517-
foreach(cell, non_null_values)
518-
{
519-
PartitionListValue *src = lfirst(cell);
520-
521-
all_values[i] = (PartitionListValue *)
522-
palloc(sizeof(PartitionListValue));
523-
all_values[i]->value = src->value;
524-
all_values[i]->index = src->index;
525-
i++;
526-
}
538+
/* ensure we found a Datum for every slot in the all_values array */
539+
Assert(j == ndatums);
527540

528-
qsort_arg(all_values, ndatums, sizeof(PartitionListValue *),
541+
qsort_arg(all_values, ndatums, sizeof(PartitionListValue),
529542
qsort_partition_list_value_cmp, (void *) key);
530543

531544
boundinfo->ndatums = ndatums;
532545
boundinfo->datums = (Datum **) palloc0(ndatums * sizeof(Datum *));
546+
boundinfo->kind = NULL;
533547
boundinfo->nindexes = ndatums;
534548
boundinfo->indexes = (int *) palloc(ndatums * sizeof(int));
535549

550+
/*
551+
* In the loop below, to save from allocating a series of small datum
552+
* arrays, here we just allocate a single array and below we'll just
553+
* assign a portion of this array per datum.
554+
*/
555+
boundDatums = (Datum *) palloc(ndatums * sizeof(Datum));
556+
536557
/*
537558
* Copy values. Canonical indexes are values ranging from 0 to (nparts -
538559
* 1) assigned to each partition such that all datums of a given partition
@@ -541,10 +562,10 @@ create_list_bounds(PartitionBoundSpec **boundspecs, int nparts,
541562
*/
542563
for (i = 0; i < ndatums; i++)
543564
{
544-
int orig_index = all_values[i]->index;
565+
int orig_index = all_values[i].index;
545566

546-
boundinfo->datums[i] = (Datum *) palloc(sizeof(Datum));
547-
boundinfo->datums[i][0] = datumCopy(all_values[i]->value,
567+
boundinfo->datums[i] = &boundDatums[i];
568+
boundinfo->datums[i][0] = datumCopy(all_values[i].value,
548569
key->parttypbyval[0],
549570
key->parttyplen[0]);
550571

@@ -555,6 +576,8 @@ create_list_bounds(PartitionBoundSpec **boundspecs, int nparts,
555576
boundinfo->indexes[i] = (*mapping)[orig_index];
556577
}
557578

579+
pfree(all_values);
580+
558581
/*
559582
* Set the canonical value for null_index, if any.
560583
*
@@ -603,10 +626,13 @@ create_range_bounds(PartitionBoundSpec **boundspecs, int nparts,
603626
PartitionRangeBound **all_bounds,
604627
*prev;
605628
int i,
606-
k;
629+
k,
630+
partnatts;
607631
int ndatums = 0;
608632
int default_index = -1;
609633
int next_index = 0;
634+
Datum *boundDatums;
635+
PartitionRangeDatumKind *boundKinds;
610636

611637
boundinfo = (PartitionBoundInfoData *)
612638
palloc0(sizeof(PartitionBoundInfoData));
@@ -707,6 +733,8 @@ create_range_bounds(PartitionBoundSpec **boundspecs, int nparts,
707733
prev = cur;
708734
}
709735

736+
pfree(all_bounds);
737+
710738
/* Update ndatums to hold the count of distinct datums. */
711739
ndatums = k;
712740

@@ -731,16 +759,24 @@ create_range_bounds(PartitionBoundSpec **boundspecs, int nparts,
731759
boundinfo->nindexes = ndatums + 1;
732760
boundinfo->indexes = (int *) palloc((ndatums + 1) * sizeof(int));
733761

762+
/*
763+
* In the loop below, to save from allocating a series of small arrays,
764+
* here we just allocate a single array for Datums and another for
765+
* PartitionRangeDatumKinds, below we'll just assign a portion of these
766+
* arrays in each loop.
767+
*/
768+
partnatts = key->partnatts;
769+
boundDatums = (Datum *) palloc(ndatums * partnatts * sizeof(Datum));
770+
boundKinds = (PartitionRangeDatumKind *) palloc(ndatums * partnatts *
771+
sizeof(PartitionRangeDatumKind));
772+
734773
for (i = 0; i < ndatums; i++)
735774
{
736775
int j;
737776

738-
boundinfo->datums[i] = (Datum *) palloc(key->partnatts *
739-
sizeof(Datum));
740-
boundinfo->kind[i] = (PartitionRangeDatumKind *)
741-
palloc(key->partnatts *
742-
sizeof(PartitionRangeDatumKind));
743-
for (j = 0; j < key->partnatts; j++)
777+
boundinfo->datums[i] = &boundDatums[i * partnatts];
778+
boundinfo->kind[i] = &boundKinds[i * partnatts];
779+
for (j = 0; j < partnatts; j++)
744780
{
745781
if (rbounds[i]->kind[j] == PARTITION_RANGE_DATUM_VALUE)
746782
boundinfo->datums[i][j] =
@@ -772,6 +808,8 @@ create_range_bounds(PartitionBoundSpec **boundspecs, int nparts,
772808
}
773809
}
774810

811+
pfree(rbounds);
812+
775813
/* Set the canonical value for default_index, if any. */
776814
if (default_index != -1)
777815
{
@@ -914,6 +952,7 @@ partition_bounds_copy(PartitionBoundInfo src,
914952
int partnatts;
915953
bool hash_part;
916954
int natts;
955+
Datum *boundDatums;
917956

918957
dest = (PartitionBoundInfo) palloc(sizeof(PartitionBoundInfoData));
919958

@@ -929,15 +968,27 @@ partition_bounds_copy(PartitionBoundInfo src,
929968

930969
if (src->kind != NULL)
931970
{
971+
PartitionRangeDatumKind *boundKinds;
972+
973+
/* only RANGE partition should have a non-NULL kind */
974+
Assert(key->strategy == PARTITION_STRATEGY_RANGE);
975+
932976
dest->kind = (PartitionRangeDatumKind **) palloc(ndatums *
933977
sizeof(PartitionRangeDatumKind *));
978+
979+
/*
980+
* In the loop below, to save from allocating a series of small arrays
981+
* for storing the PartitionRangeDatumKind, we allocate a single chunk
982+
* here and use a smaller portion of it for each datum.
983+
*/
984+
boundKinds = (PartitionRangeDatumKind *) palloc(ndatums * partnatts *
985+
sizeof(PartitionRangeDatumKind));
986+
934987
for (i = 0; i < ndatums; i++)
935988
{
936-
dest->kind[i] = (PartitionRangeDatumKind *) palloc(partnatts *
937-
sizeof(PartitionRangeDatumKind));
938-
989+
dest->kind[i] = &boundKinds[i * partnatts];
939990
memcpy(dest->kind[i], src->kind[i],
940-
sizeof(PartitionRangeDatumKind) * key->partnatts);
991+
sizeof(PartitionRangeDatumKind) * partnatts);
941992
}
942993
}
943994
else
@@ -949,12 +1000,13 @@ partition_bounds_copy(PartitionBoundInfo src,
9491000
*/
9501001
hash_part = (key->strategy == PARTITION_STRATEGY_HASH);
9511002
natts = hash_part ? 2 : partnatts;
1003+
boundDatums = palloc(ndatums * natts * sizeof(Datum));
9521004

9531005
for (i = 0; i < ndatums; i++)
9541006
{
9551007
int j;
9561008

957-
dest->datums[i] = (Datum *) palloc(sizeof(Datum) * natts);
1009+
dest->datums[i] = &boundDatums[i * natts];
9581010

9591011
for (j = 0; j < natts; j++)
9601012
{
@@ -3682,8 +3734,8 @@ partition_hash_bsearch(PartitionBoundInfo boundinfo,
36823734
static int32
36833735
qsort_partition_hbound_cmp(const void *a, const void *b)
36843736
{
3685-
PartitionHashBound *h1 = (*(PartitionHashBound *const *) a);
3686-
PartitionHashBound *h2 = (*(PartitionHashBound *const *) b);
3737+
PartitionHashBound *const h1 = (PartitionHashBound *const) a;
3738+
PartitionHashBound *const h2 = (PartitionHashBound *const) b;
36873739

36883740
return partition_hbound_cmp(h1->modulus, h1->remainder,
36893741
h2->modulus, h2->remainder);
@@ -3697,8 +3749,8 @@ qsort_partition_hbound_cmp(const void *a, const void *b)
36973749
static int32
36983750
qsort_partition_list_value_cmp(const void *a, const void *b, void *arg)
36993751
{
3700-
Datum val1 = (*(PartitionListValue *const *) a)->value,
3701-
val2 = (*(PartitionListValue *const *) b)->value;
3752+
Datum val1 = ((PartitionListValue *const) a)->value,
3753+
val2 = ((PartitionListValue *const) b)->value;
37023754
PartitionKey key = (PartitionKey) arg;
37033755

37043756
return DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],

0 commit comments

Comments
 (0)