From 286310e6a52af7883d20899163aaa349b479d614 Mon Sep 17 00:00:00 2001 From: Amit Khandekar Date: Wed, 9 Sep 2020 11:52:59 +0800 Subject: [PATCH 1/2] Avoid redundant tuple copy while sending tuples to Gather If the tuple to be sent is a heap tuple, get the pointer to the minimal tuple data from the heap tuple, and use this as the source data for shm_mq_send(). This allows us to prevent a tuple copy when the slot is a heap tuple slot. Device a new function ExecFetchSlotMinimalTupleData() that tries to send an in-place minimal tuple data or returns a copy only if the slot is something other than a heap tuple or minimal tuple slot. This shows between 4 to 10% speed up for simple non-aggregate parallel queries. --- src/backend/executor/execTuples.c | 59 +++++++++++++++++++++++++++++++ src/backend/executor/tqueue.c | 17 +++++---- src/include/executor/tuptable.h | 2 ++ 3 files changed, 71 insertions(+), 7 deletions(-) diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 4c90ac5236..55a6652b4e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -1682,6 +1682,65 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot, } } +/* -------------------------------- + * ExecFetchSlotMinimalTupleData + * Return minimal tuple data, or the data belonging to minimal tuple + * section if it's a heap tuple slot. + * + * This function is similar to ExecFetchSlotMinimalTuple(), but it goes a + * step further in trying to avoid redundant tuple copy. It does this by + * returning the in-place minimal tuple data region if it's a heap tuple. + * If the slot is neither a minimal tuple nor heap tuple slot, a minimal + * tuple copy is returned, and should_free is set to true. Callers can use + * this if they only want the underlying minimal tuple data. + * One use is where minimal tuple data is sent through the gather queues. + * The receiver end can then treat the data as a MinimalTupleData, but + * they should update it's t_len field, because the data might have + * originally belonged to a heap tuple rather than minimal tuple. + */ +char * +ExecFetchSlotMinimalTupleData(TupleTableSlot *slot, uint32 *len, + bool *shouldFree) +{ + /* + * sanity checks + */ + Assert(slot != NULL); + Assert(!TTS_EMPTY(slot)); + + if (slot->tts_ops->get_heap_tuple) + { + HeapTuple htuple; + + if (shouldFree) + *shouldFree = false; + htuple = slot->tts_ops->get_heap_tuple(slot); + *len = htuple->t_len - MINIMAL_TUPLE_OFFSET; + + return (char*) htuple->t_data + MINIMAL_TUPLE_OFFSET; + } + else + { + MinimalTuple mtuple; + + if (slot->tts_ops->get_minimal_tuple) + { + if (shouldFree) + *shouldFree = false; + mtuple = slot->tts_ops->get_minimal_tuple(slot); + } + else + { + if (shouldFree) + *shouldFree = true; + mtuple = slot->tts_ops->copy_minimal_tuple(slot); + } + *len = mtuple->t_len; + + return (char *) mtuple; + } +} + /* -------------------------------- * ExecFetchSlotHeapTupleDatum * Fetch the slot's tuple as a composite-type Datum. diff --git a/src/backend/executor/tqueue.c b/src/backend/executor/tqueue.c index 30a264ebea..c70ee0f39a 100644 --- a/src/backend/executor/tqueue.c +++ b/src/backend/executor/tqueue.c @@ -54,16 +54,17 @@ static bool tqueueReceiveSlot(TupleTableSlot *slot, DestReceiver *self) { TQueueDestReceiver *tqueue = (TQueueDestReceiver *) self; - MinimalTuple tuple; + char *minimal_tuple_data; + uint32 len; shm_mq_result result; bool should_free; - /* Send the tuple itself. */ - tuple = ExecFetchSlotMinimalTuple(slot, &should_free); - result = shm_mq_send(tqueue->queue, tuple->t_len, tuple, false); + /* Send the minimal tuple data. */ + minimal_tuple_data = ExecFetchSlotMinimalTupleData(slot, &len, &should_free); + result = shm_mq_send(tqueue->queue, len, minimal_tuple_data, false); if (should_free) - pfree(tuple); + pfree(minimal_tuple_data); /* Check for failure. */ if (result == SHM_MQ_DETACHED) @@ -201,10 +202,12 @@ TupleQueueReaderNext(TupleQueueReader *reader, bool nowait, bool *done) /* * Return a pointer to the queue memory directly (which had better be - * sufficiently aligned). + * sufficiently aligned). Also, the sender might not have updated the t_len + * if the data belonged to a heap tuple rather than a minimal tuple. So + * update it now. */ tuple = (MinimalTuple) data; - Assert(tuple->t_len == nbytes); + tuple->t_len = nbytes; return tuple; } diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h index f7df70b5ab..da78929035 100644 --- a/src/include/executor/tuptable.h +++ b/src/include/executor/tuptable.h @@ -325,6 +325,8 @@ extern void ExecStoreHeapTupleDatum(Datum data, TupleTableSlot *slot); extern HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree); extern MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot *slot, bool *shouldFree); +extern char *ExecFetchSlotMinimalTupleData(TupleTableSlot *slot, uint32 *len, + bool *shouldFree); extern Datum ExecFetchSlotHeapTupleDatum(TupleTableSlot *slot); extern void slot_getmissingattrs(TupleTableSlot *slot, int startAttNum, int lastAttNum); -- 2.17.1