diff options
author | Andres Freund | 2019-09-20 02:41:23 +0000 |
---|---|---|
committer | Andres Freund | 2019-09-20 04:25:10 +0000 |
commit | 0198b4bbb660d23a20717fd315735bc2c6ccce73 (patch) | |
tree | 624bb2a6e325ffaeed8c8e81fb73901c0e7c758e | |
parent | 1b5bee11be1f685f6e53907e602b1be49501bccb (diff) |
WIP: Add support for copying node tree into single allocation.header
As an example as to why one would want that, here's a
MemoryContextStats for the same query being added to the
plancache. The query is just a randomly selected one among the queries
issued by \d+.
PREPARE foo AS SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, c.relhastriggers, c.relrowsecurity, c.relforcerowsecurity, false AS relhasoids, c.relispartition, pg_catalog.array_to_string(c.reloptions || array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', '), c.reltablespace, CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, c.relpersistence, c.relreplident, am.amname FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid) LEFT JOIN pg_catalog.pg_am am ON (c.relam = am.oid) WHERE c.oid = '1259';
EXECUTE foo ;
With single-allocation:
CachedPlan: 24504 total in 2 blocks; 664 free (0 chunks); 23840 used
Grand total: 24504 bytes in 2 blocks; 664 free (0 chunks); 23840 used
Default:
CachedPlan: 65536 total in 7 blocks; 16016 free (0 chunks); 49520 used
Grand total: 65536 bytes in 7 blocks; 16016 free (0 chunks); 49520 used
Author:
Reviewed-By:
Discussion: https://postgr.es/m/
Backpatch:
-rw-r--r-- | src/backend/nodes/copyfuncs_new.c | 274 | ||||
-rw-r--r-- | src/backend/utils/cache/plancache.c | 8 | ||||
-rw-r--r-- | src/include/nodes/nodes.h | 3 |
3 files changed, 279 insertions, 6 deletions
diff --git a/src/backend/nodes/copyfuncs_new.c b/src/backend/nodes/copyfuncs_new.c index 77cd6b6846..95532f4417 100644 --- a/src/backend/nodes/copyfuncs_new.c +++ b/src/backend/nodes/copyfuncs_new.c @@ -22,6 +22,12 @@ static void nodecopy_fields(CopyNodeContext *context, Node *dst, const Node *src static List* nodecopy_list(CopyNodeContext *context, const List *obj, NodeTag tag); static void nodecopy_value_union(CopyNodeContext *context, Value *dst, const Value *src); +static void nodesize_rec(CopyNodeContext *context, const Node *obj); +static void nodesize_fields(CopyNodeContext *context, const Node *obj, const TINodeType *type_info); +static void nodesize_list(CopyNodeContext *context, const List *obj, NodeTag tag); +static void nodesize_value_union(CopyNodeContext *context, const Value *src); +static void nodesize_count(CopyNodeContext *context, size_t size, size_t align); + #define BITMAPSET_SIZE(nwords) \ (offsetof(Bitmapset, words) + (nwords) * sizeof(bitmapword)) @@ -40,16 +46,280 @@ copyObjectImpl(const void *from) return nodecopy_new_rec(&context, from); } +void * +copyObjectRoImpl(const void *obj) +{ +#if 0 + CopyNodeContext context = {0}; + + /* count space */ + nodesize_rec(&context, obj); + + /* allocate memory in one go */ + context.space = palloc_extended(context.required_space, + MCXT_ALLOC_HUGE); + + return nodecopy_new_rec(&context, obj); +#else + return copyObjectImpl(obj); +#endif +} + +static void +nodesize_rec(CopyNodeContext *context, const Node *obj) +{ + const TINodeType *type_info; + NodeTag tag; + + if (obj == NULL) + return; + + tag = nodeTag(obj); + + /* Guard against stack overflow due to overly complex expressions */ + check_stack_depth(); + + switch (tag) + { + case T_List: + case T_OidList: + case T_IntList: + nodesize_list(context, (List *) obj, tag); + return; + + default: + break; + } + + type_info = &ti_node_types[tag]; + + Assert(type_info->size != TYPE_SIZE_UNKNOWN); + + nodesize_count(context, type_info->size, MAXIMUM_ALIGNOF); + nodesize_fields(context, obj, type_info); +} + +static void +nodesize_count(CopyNodeContext *context, size_t size, size_t align) +{ + size_t alignup; + + alignup = TYPEALIGN(align, context->required_space) - context->required_space; + Assert(alignup < MAXIMUM_ALIGNOF); + context->required_space += alignup; + context->required_space += size; +} + +static void +nodesize_fields(CopyNodeContext *context, const Node *obj, const TINodeType *type_info) +{ + const TIStructField *field_info = &ti_struct_fields[type_info->first_field_at]; + + for (int i = 0; i < type_info->num_fields; i++, field_info++) + { + const void *field_ptr; + + // FIXME: ExtensibleNode needs to call callbacks, or be reimplemented + + if (field_info->flags & TYPE_COPY_IGNORE) + continue; + + field_ptr = ((char *) obj + field_info->offset); + + switch (field_info->known_type_id) + { + case KNOWN_TYPE_NODE: + { + NodeTag sub_tag; + const TINodeType *sub_type_info; + + Assert(field_info->type_id != TYPE_ID_UNKNOWN); + + if (field_info->offset == 0) + sub_tag = field_info->type_id; + else + { + sub_tag = nodeTag(field_ptr); + + Assert(ti_node_types[sub_tag].size == + ti_node_types[field_info->type_id].size); + } + + sub_type_info = &ti_node_types[sub_tag]; + + nodesize_fields(context, + (const Node *) field_ptr, + sub_type_info); + + break; + } + + case KNOWN_TYPE_DATUM: + { + const Const *cptr = castNode(Const, (Node *) obj); + + if (!cptr->constbyval && cptr->constisnull) + { + /* passed by reference, account for space */ + // FIXME: could reduce alignment, but would require additional information + nodesize_count(context, + datumGetSize(cptr->constvalue, + cptr->constbyval, + cptr->constlen), + MAXIMUM_ALIGNOF); + } + } + + break; + + case KNOWN_TYPE_VALUE_UNION: + Assert(IsA(obj, Integer) || IsA(obj, Float) || + IsA(obj, String) || IsA(obj, BitString) || + IsA(obj, Null)); + + nodesize_value_union(context, (const Value *) obj); + break; + + case KNOWN_TYPE_P_PGARR: + if (*(PgArrBase **) field_ptr == NULL) + break; + + Assert(field_info->elem_size > 0); + Assert(field_info->elem_known_type_id > 0); + + nodesize_count(context, + MAXALIGN(sizeof(PgArrBase)) + + field_info->elem_size * (*(PgArrBase **) field_ptr)->size, + MAXIMUM_ALIGNOF); + break; + + case KNOWN_TYPE_P_NODE: + if (*(Node **) field_ptr == NULL) + break; + nodesize_rec(context, *(Node **) field_ptr); + break; + + case KNOWN_TYPE_P_CHAR: + if (*(char **) field_ptr == NULL) + break; + nodesize_count(context, + strlen(*(char **) field_ptr) + 1, + 1); + break; + + case KNOWN_TYPE_P_BITMAPSET: + if (*(const Bitmapset **) field_ptr == NULL) + break; + + nodesize_count(context, + BITMAPSET_SIZE((*(const Bitmapset **) field_ptr)->nwords), + MAXIMUM_ALIGNOF); + break; + + default: + if (field_info->flags & (TYPE_COPY_FORCE_SCALAR || + TYPE_CAT_SCALAR)) + Assert(field_info->size != TYPE_SIZE_UNKNOWN); + else + elog(ERROR, "don't know how to copy field %s %s->%s", + ti_strings[field_info->type].string, + ti_strings[type_info->name].string, + ti_strings[field_info->name].string); + } + } +} + +static void +nodesize_list(CopyNodeContext *context, const List *obj, NodeTag tag) +{ + /* XXX: this is copying implementation details from new_list. */ + nodesize_count(context, + offsetof(List, initial_elements) + + obj->length * sizeof(ListCell), + MAXIMUM_ALIGNOF); + + switch (tag) + { + case T_List: + for (int i = 0; i < obj->length; i++) + { + nodesize_rec(context, lfirst(&obj->elements[i])); + } + break; + + case T_OidList: + case T_IntList: + /* already accounted for */ + break; + + default: + pg_unreachable(); + } +} + +static void +nodesize_value_union(CopyNodeContext *context, const Value *obj) +{ + switch (nodeTag(obj)) + { + case T_Null: + case T_Integer: + /* part of struct Value itself */ + break; + + case T_Float: + case T_String: + case T_BitString: + if (obj->val.str != NULL) + nodesize_count(context, + strlen(obj->val.str) + 1, + 1); + break; + + default: + pg_unreachable(); + break; + } +} + static inline void* nodecopy_alloc(CopyNodeContext *context, size_t size, size_t align) { - return palloc(size); + if (context->space == NULL) + return palloc(size); + else + { + void *ret; + size_t alignup; + + alignup = TYPEALIGN(align, context->used_space) - context->used_space; + Assert(alignup < MAXIMUM_ALIGNOF); + + Assert(context->used_space + alignup <= context->required_space); + context->used_space += alignup; + + ret = context->space + context->used_space; + + Assert(context->used_space + size <= context->required_space); + context->used_space += size; + + return ret; + } } static inline void* nodecopy_alloc0(CopyNodeContext *context, size_t size, size_t align) { - return palloc0(size); + if (context->space == NULL) + return palloc0(size); + else + { + void *alloc = nodecopy_alloc(context, size, align); + + memset(alloc, 0, size); + + return alloc; + } } static Node* diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c index abc3062892..a41117963d 100644 --- a/src/backend/utils/cache/plancache.c +++ b/src/backend/utils/cache/plancache.c @@ -190,7 +190,7 @@ CreateCachedPlan(RawStmt *raw_parse_tree, plansource = (CachedPlanSource *) palloc0(sizeof(CachedPlanSource)); plansource->magic = CACHEDPLANSOURCE_MAGIC; - plansource->raw_parse_tree = copyObject(raw_parse_tree); + plansource->raw_parse_tree = copyObjectRo(raw_parse_tree); plansource->query_string = pstrdup(query_string); MemoryContextSetIdentifier(source_context, plansource->query_string); plansource->commandTag = commandTag; @@ -373,7 +373,7 @@ CompleteCachedPlan(CachedPlanSource *plansource, "CachedPlanQuery", ALLOCSET_START_SMALL_SIZES); MemoryContextSwitchTo(querytree_context); - querytree_list = copyObject(querytree_list); + querytree_list = copyObjectRo(querytree_list); } plansource->query_context = querytree_context; @@ -954,7 +954,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist, */ MemoryContextSwitchTo(plan_context); - plist = copyObject(plist); + plist = copyObjectRo(plist); } else plan_context = CurrentMemoryContext; @@ -1347,7 +1347,7 @@ CopyCachedPlan(CachedPlanSource *plansource) newsource = (CachedPlanSource *) palloc0(sizeof(CachedPlanSource)); newsource->magic = CACHEDPLANSOURCE_MAGIC; - newsource->raw_parse_tree = copyObject(plansource->raw_parse_tree); + newsource->raw_parse_tree = copyObjectRo(plansource->raw_parse_tree); newsource->query_string = pstrdup(plansource->query_string); MemoryContextSetIdentifier(source_context, newsource->query_string); newsource->commandTag = plansource->commandTag; diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 563eeb40e6..ffe6d61f11 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -634,12 +634,15 @@ extern uintptr_t readDatum(bool typbyval); * nodes/copyfuncs.c */ extern void *copyObjectImpl(const void *obj); +extern void *copyObjectRoImpl(const void *obj); /* cast result back to argument type, if supported by compiler */ #ifdef HAVE_TYPEOF #define copyObject(obj) ((typeof(obj)) copyObjectImpl(obj)) +#define copyObjectRo(obj) ((typeof(obj)) copyObjectRoImpl(obj)) #else #define copyObject(obj) copyObjectImpl(obj) +#define copyObjectRo(obj) copyObjectRoImpl(obj) #endif /* |