Skip to content

Commit 6b013c1

Browse files
committed
Speedup an array selectivity estimation.
Cache statistics during an array selectivity estimation due to repetative Var op Const estimations. Reduce cycles to detoast and unpack statistics, especially big ones like histogram or MCV arrays. Reuse memory allocated for array's element selectivity estimation. Remember, we have a community's patch & thread about selectivity memory context. But for now it is hard to say how quickly it can be pushed. So, use simple hack now. It saves almost 50% of allocated memory in the case labelled as 'SHRDM-3141'.
1 parent a3699da commit 6b013c1

File tree

3 files changed

+195
-37
lines changed

3 files changed

+195
-37
lines changed

src/backend/utils/adt/selfuncs.c

Lines changed: 49 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1930,6 +1930,8 @@ scalararraysel(PlannerInfo *root,
19301930
Datum *elem_values;
19311931
bool *elem_nulls;
19321932
int i;
1933+
List *args;
1934+
Const *c;
19331935

19341936
if (arrayisnull) /* qual can't succeed if null array */
19351937
return (Selectivity) 0.0;
@@ -1957,48 +1959,56 @@ scalararraysel(PlannerInfo *root,
19571959
*/
19581960
s1 = s1disjoint = (useOr ? 0.0 : 1.0);
19591961

1960-
for (i = 0; i < num_elems; i++)
1961-
{
1962-
List *args;
1963-
Selectivity s2;
1962+
set_attstatsslot_cache_mode(true);
19641963

1965-
args = list_make2(leftop,
1966-
makeConst(nominal_element_type,
1967-
-1,
1968-
nominal_element_collation,
1969-
elmlen,
1970-
elem_values[i],
1971-
elem_nulls[i],
1972-
elmbyval));
1973-
if (is_join_clause)
1974-
s2 = DatumGetFloat8(FunctionCall5Coll(&oprselproc,
1975-
clause->inputcollid,
1976-
PointerGetDatum(root),
1977-
ObjectIdGetDatum(operator),
1978-
PointerGetDatum(args),
1979-
Int16GetDatum(jointype),
1980-
PointerGetDatum(sjinfo)));
1981-
else
1982-
s2 = DatumGetFloat8(FunctionCall4Coll(&oprselproc,
1983-
clause->inputcollid,
1984-
PointerGetDatum(root),
1985-
ObjectIdGetDatum(operator),
1986-
PointerGetDatum(args),
1987-
Int32GetDatum(varRelid)));
1964+
c = makeConst(nominal_element_type, -1, nominal_element_collation,
1965+
elmlen, (Datum) 0, true, elmbyval);
1966+
args = list_make2(leftop, c);
19881967

1989-
if (useOr)
1990-
{
1991-
s1 = s1 + s2 - s1 * s2;
1992-
if (isEquality)
1993-
s1disjoint += s2;
1994-
}
1995-
else
1968+
PG_TRY();
1969+
{
1970+
for (i = 0; i < num_elems; i++)
19961971
{
1997-
s1 = s1 * s2;
1998-
if (isInequality)
1999-
s1disjoint += s2 - 1.0;
1972+
Selectivity s2;
1973+
1974+
c->constvalue = elem_values[i];
1975+
c->constisnull = elem_nulls[i];
1976+
1977+
if (is_join_clause)
1978+
s2 = DatumGetFloat8(FunctionCall5Coll(&oprselproc,
1979+
clause->inputcollid,
1980+
PointerGetDatum(root),
1981+
ObjectIdGetDatum(operator),
1982+
PointerGetDatum(args),
1983+
Int16GetDatum(jointype),
1984+
PointerGetDatum(sjinfo)));
1985+
else
1986+
s2 = DatumGetFloat8(FunctionCall4Coll(&oprselproc,
1987+
clause->inputcollid,
1988+
PointerGetDatum(root),
1989+
ObjectIdGetDatum(operator),
1990+
PointerGetDatum(args),
1991+
Int32GetDatum(varRelid)));
1992+
1993+
if (useOr)
1994+
{
1995+
s1 = s1 + s2 - s1 * s2;
1996+
if (isEquality)
1997+
s1disjoint += s2;
1998+
}
1999+
else
2000+
{
2001+
s1 = s1 * s2;
2002+
if (isInequality)
2003+
s1disjoint += s2 - 1.0;
2004+
}
20002005
}
20012006
}
2007+
PG_FINALLY();
2008+
{
2009+
set_attstatsslot_cache_mode(false);
2010+
}
2011+
PG_END_TRY();
20022012

20032013
/* accept disjoint-probability estimate if in range */
20042014
if ((useOr ? isEquality : isInequality) &&
@@ -2053,6 +2063,8 @@ scalararraysel(PlannerInfo *root,
20532063
PointerGetDatum(args),
20542064
Int32GetDatum(varRelid)));
20552065

2066+
pfree(args);
2067+
20562068
if (useOr)
20572069
{
20582070
s1 = s1 + s2 - s1 * s2;

src/backend/utils/cache/lsyscache.c

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include "utils/builtins.h"
4545
#include "utils/catcache.h"
4646
#include "utils/datum.h"
47+
#include "utils/inval.h"
4748
#include "utils/fmgroids.h"
4849
#include "utils/lsyscache.h"
4950
#include "utils/syscache.h"
@@ -3180,6 +3181,137 @@ get_attavgwidth(Oid relid, AttrNumber attnum)
31803181
return 0;
31813182
}
31823183

3184+
typedef struct AttStatSlotHashKey
3185+
{
3186+
Oid relid;
3187+
int attnum;
3188+
int reqkind;
3189+
Oid reqop;
3190+
int flags;
3191+
} AttStatSlotHashKey;
3192+
3193+
typedef struct AttStatSlotHashEntry
3194+
{
3195+
AttStatSlotHashKey key;
3196+
AttStatsSlot sslot;
3197+
} AttStatSlotHashEntry;
3198+
3199+
static bool attstatsslot_needs_cache = false;
3200+
static HTAB *slot_htab = NULL;
3201+
3202+
void
3203+
set_attstatsslot_cache_mode(bool need_cache)
3204+
{
3205+
/* It should be a usage error or a bug */
3206+
Assert(!(need_cache && attstatsslot_needs_cache));
3207+
3208+
if (!attstatsslot_needs_cache && !need_cache)
3209+
/* Nothing to do */
3210+
return;
3211+
3212+
attstatsslot_needs_cache = need_cache;
3213+
3214+
if (!need_cache && slot_htab != NULL &&
3215+
hash_get_num_entries(slot_htab) > 0)
3216+
{
3217+
HASH_SEQ_STATUS hash_seq;
3218+
AttStatSlotHashEntry *entry;
3219+
3220+
/*
3221+
* It seems we have only few entries here. So, instead of removing the
3222+
* whole table, just remove entries one-by-one.
3223+
*/
3224+
3225+
hash_seq_init(&hash_seq, slot_htab);
3226+
while ((entry = hash_seq_search(&hash_seq)) != NULL)
3227+
{
3228+
bool found;
3229+
3230+
free_attstatsslot(&entry->sslot);
3231+
3232+
(void) hash_search(slot_htab, &entry->key, HASH_REMOVE, &found);
3233+
3234+
if (!found)
3235+
elog(PANIC, "Hash table is corrupted");
3236+
}
3237+
}
3238+
}
3239+
3240+
static void
3241+
cache_attstatslot(AttStatsSlot *sslot,
3242+
Oid relid, int attnum, int reqkind, Oid reqop, int flags)
3243+
{
3244+
AttStatSlotHashEntry *entry;
3245+
AttStatSlotHashKey key = {.relid = relid, .attnum = attnum,
3246+
.reqkind = reqkind, .reqop = reqop,
3247+
.flags = flags};
3248+
bool found;
3249+
3250+
entry = hash_search(slot_htab, &key, HASH_ENTER, &found);
3251+
Assert(!found);
3252+
3253+
memcpy(&entry->sslot, sslot, sizeof(AttStatsSlot));
3254+
}
3255+
3256+
static void
3257+
AttStatSlotCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
3258+
{
3259+
HASH_SEQ_STATUS status;
3260+
AttStatSlotHashEntry *entry;
3261+
3262+
if (slot_htab == NULL)
3263+
return;
3264+
3265+
/* Currently we just flush all entries; hard to be smarter ... */
3266+
hash_seq_init(&status, slot_htab);
3267+
3268+
while ((entry = (AttStatSlotHashEntry *) hash_seq_search(&status)) != NULL)
3269+
{
3270+
free_attstatsslot(&entry->sslot);
3271+
3272+
if (hash_search(slot_htab,
3273+
&entry->key,
3274+
HASH_REMOVE, NULL) == NULL)
3275+
elog(ERROR, "hash table corrupted");
3276+
}
3277+
}
3278+
3279+
static bool
3280+
get_cached_attstatslot(AttStatsSlot *sslot,
3281+
Oid relid, int attnum, int reqkind, Oid reqop, int flags)
3282+
{
3283+
AttStatSlotHashEntry *entry;
3284+
AttStatSlotHashKey key = {.relid = relid, .attnum = attnum,
3285+
.reqkind = reqkind, .reqop = reqop,
3286+
.flags = flags};
3287+
bool found;
3288+
3289+
if (slot_htab == NULL)
3290+
{
3291+
HASHCTL ctl;
3292+
3293+
ctl.keysize = sizeof(AttStatSlotHashKey);
3294+
ctl.entrysize = sizeof(AttStatSlotHashEntry);
3295+
3296+
slot_htab = hash_create("AttStatSlot hash", 16, &ctl,
3297+
HASH_ELEM | HASH_BLOBS);
3298+
3299+
/* To stay consistent clear the cache on demand */
3300+
CacheRegisterSyscacheCallback(STATRELATTINH,
3301+
AttStatSlotCacheCallback, (Datum) 0);
3302+
}
3303+
3304+
entry = hash_search(slot_htab, &key, HASH_FIND, &found);
3305+
3306+
if (found)
3307+
{
3308+
memcpy(sslot, &entry->sslot, sizeof(AttStatsSlot));
3309+
return true;
3310+
}
3311+
3312+
return false;
3313+
}
3314+
31833315
/*
31843316
* get_attstatsslot
31853317
*
@@ -3246,6 +3378,12 @@ get_attstatsslot(AttStatsSlot *sslot, HeapTuple statstuple,
32463378
/* initialize *sslot properly */
32473379
memset(sslot, 0, sizeof(AttStatsSlot));
32483380

3381+
/* Try to return cached statistics */
3382+
if (attstatsslot_needs_cache &&
3383+
get_cached_attstatslot(sslot, stats->starelid,
3384+
stats->staattnum, reqkind, reqop, flags))
3385+
return true;
3386+
32493387
for (i = 0; i < STATISTIC_NUM_SLOTS; i++)
32503388
{
32513389
if ((&stats->stakind1)[i] == reqkind &&
@@ -3333,6 +3471,10 @@ get_attstatsslot(AttStatsSlot *sslot, HeapTuple statstuple,
33333471
sslot->numbers_arr = statarray;
33343472
}
33353473

3474+
if (attstatsslot_needs_cache)
3475+
cache_attstatslot(sslot, stats->starelid,
3476+
stats->staattnum, reqkind, reqop, flags);
3477+
33363478
return true;
33373479
}
33383480

@@ -3343,6 +3485,9 @@ get_attstatsslot(AttStatsSlot *sslot, HeapTuple statstuple,
33433485
void
33443486
free_attstatsslot(AttStatsSlot *sslot)
33453487
{
3488+
if (attstatsslot_needs_cache)
3489+
return;
3490+
33463491
/* The values[] array was separately palloc'd by deconstruct_array */
33473492
if (sslot->values)
33483493
pfree(sslot->values);

src/include/utils/lsyscache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ extern Oid getBaseType(Oid typid);
188188
extern Oid getBaseTypeAndTypmod(Oid typid, int32 *typmod);
189189
extern int32 get_typavgwidth(Oid typid, int32 typmod);
190190
extern int32 get_attavgwidth(Oid relid, AttrNumber attnum);
191+
extern void set_attstatsslot_cache_mode(bool need_cache);
191192
extern bool get_attstatsslot(AttStatsSlot *sslot, HeapTuple statstuple,
192193
int reqkind, Oid reqop, int flags);
193194
extern void free_attstatsslot(AttStatsSlot *sslot);

0 commit comments

Comments
 (0)