From 185a6c8ee89aecb49b61b35da3c3c5562a558bd8 Mon Sep 17 00:00:00 2001 From: Alexander Korotkov Date: Mon, 27 Mar 2023 15:05:25 +0300 Subject: [PATCH 1/2] Improve lazy tuple slot This commit implements two improvements to lazy tuple slot implementation. * Replace macros with inline function to prevent multiple evaluation of the argument. * Introdue a new lazy tuple slot constructor on the base of existing normal slot, not a callback. Reported-by: Andres Freund Discussion: https://postgr.es/m/20230323003003.plgaxjqahjgkuxrk%40awork3.anarazel.de --- src/backend/access/heap/heapam_handler.c | 4 +- src/backend/executor/nodeModifyTable.c | 4 +- src/include/executor/tuptable.h | 57 +++++++++++++++++------- 3 files changed, 45 insertions(+), 20 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 9e690074e94..2bddf562c2f 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -330,7 +330,7 @@ heapam_tuple_delete(Relation relation, ItemPointer tid, CommandId cid, Assert(wait); - evalSlot = LAZY_TTS_EVAL(lockedSlot); + evalSlot = LazyTupleTableSlotEval(lockedSlot); result = heapam_tuple_lock_internal(relation, tid, snapshot, evalSlot, cid, LockTupleExclusive, LockWaitBlock, @@ -403,7 +403,7 @@ heapam_tuple_update(Relation relation, ItemPointer otid, TupleTableSlot *slot, Assert(wait); - evalSlot = LAZY_TTS_EVAL(lockedSlot); + evalSlot = LazyTupleTableSlotEval(lockedSlot); result = heapam_tuple_lock_internal(relation, otid, snapshot, evalSlot, cid, *lockmode, LockWaitBlock, diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index e3503756818..2f6c97a8404 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1365,7 +1365,7 @@ ExecDeleteAct(ModifyTableContext *context, ResultRelInfo *resultRelInfo, if (lockUpdated) { - MAKE_LAZY_TTS(&lazyEPQSlot, GetEPQSlot, &slotArg); + MakeLazyTupleTableSlotWithCallback(&lazyEPQSlot, GetEPQSlot, &slotArg); lazyEPQSlotPtr = &lazyEPQSlot; } else @@ -2101,7 +2101,7 @@ lreplace: */ if (lockUpdated) { - MAKE_LAZY_TTS(&lazyEPQSlot, GetEPQSlot, &slotArg); + MakeLazyTupleTableSlotWithCallback(&lazyEPQSlot, GetEPQSlot, &slotArg); lazyEPQSlotPtr = &lazyEPQSlot; } else diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h index c61734a15d4..30729f7a076 100644 --- a/src/include/executor/tuptable.h +++ b/src/include/executor/tuptable.h @@ -307,9 +307,11 @@ typedef struct MinimalTupleTableSlot * likely will reain undemanded. Preallocating such slot would be a waste of * resources in the majority of cases. Lazy slot is aimed to resolve this * problem. It is basically a promise to allocate the slot once it's needed. - * Once callee needs the slot, it could get it using LAZY_TTS_EVAL(lazySlot) - * macro. + * Once callee needs the slot, it could get it using + * LazyTupleTableSlotEval(lazySlot) function. */ +typedef TupleTableSlot *(*LazyTupleTableSlotCallback) (void *arg); + typedef struct { TupleTableSlot *slot; /* cached slot or NULL if not yet allocated */ @@ -318,25 +320,48 @@ typedef struct } LazyTupleTableSlot; /* - * A constructor for the lazy slot. + * Initialize LazyTupleTableSlot with callback and argument. + */ +static inline void +MakeLazyTupleTableSlotWithCallback(LazyTupleTableSlot *lazySlot, + LazyTupleTableSlotCallback callback, + void *arg) +{ + lazySlot->slot = NULL; + lazySlot->getSlot = callback; + lazySlot->getSlotArg = arg; +} + +/* + * Initialize LazyTupleTableSlot with existing slot. */ -#define MAKE_LAZY_TTS(lazySlot, callback, arg) \ - do { \ - (lazySlot)->slot = NULL; \ - (lazySlot)->getSlot = callback; \ - (lazySlot)->getSlotArg = arg; \ - } while (false) +static inline void +MakeLazyTupleTableSlotWithSlot(LazyTupleTableSlot *lazySlot, + TupleTableSlot *slot) +{ + lazySlot->slot = slot; + lazySlot->getSlot = NULL; + lazySlot->getSlotArg = NULL; +} /* - * Macro for lazy slot evaluation. NULL lazy slot evaluates to NULL slot. + * Function for lazy slot evaluation. NULL lazy slot evaluates to NULL slot. * Cached version is used if present. Use the callback otherwise. */ -#define LAZY_TTS_EVAL(lazySlot) \ - ((lazySlot) ? \ - ((lazySlot)->slot ? \ - (lazySlot)->slot : \ - ((lazySlot)->slot = (lazySlot)->getSlot((lazySlot)->getSlotArg))) : \ - NULL) +static inline TupleTableSlot * +LazyTupleTableSlotEval(LazyTupleTableSlot *lazySlot) +{ + if (!lazySlot) + return NULL; + + if (!lazySlot->slot) + { + Assert(lazySlot->getSlot); + lazySlot->slot = lazySlot->getSlot(lazySlot->getSlotArg); + } + Assert(lazySlot->slot); + return lazySlot->slot; +} /* in executor/execTuples.c */ extern TupleTableSlot *MakeTupleTableSlot(TupleDesc tupleDesc, -- 2.37.1 (Apple Git-137.1)