Move TupleTableSlots boolean member into one flag variable.
authorAndres Freund <andres@anarazel.de>
Mon, 15 Oct 2018 22:24:33 +0000 (15:24 -0700)
committerAndres Freund <andres@anarazel.de>
Tue, 16 Oct 2018 01:23:25 +0000 (18:23 -0700)
There's several reasons for this change:
1) It reduces the total size of TupleTableSlot / reduces alignment
   padding, making the commonly accessed members fit into a single
   cacheline (but we currently do not force proper alignment, so
   that's not yet guaranteed to be helpful)
2) Combining the booleans into a flag allows to combine read/writes
   from memory.
3) With the upcoming slot abstraction changes, it allows to have core
   and extended flags, in a memory efficient way.

Author: Ashutosh Bapat and Andres Freund
Discussion: https://postgr.es/m/20180220224318.gw4oe5jadhpmcdnm@alap3.anarazel.de

src/backend/access/common/heaptuple.c
src/backend/executor/execTuples.c
src/backend/executor/nodeAgg.c
src/backend/executor/nodeModifyTable.c
src/backend/jit/llvm/llvmjit_deform.c
src/backend/jit/llvm/llvmjit_expr.c
src/include/executor/executor.h
src/include/executor/tuptable.h

index 6b4863b33a9d70651d08094099b614efda443985..15444cf582446b2bb7e8f842941a75ef880a6ad5 100644 (file)
@@ -1391,7 +1391,7 @@ slot_deform_tuple(TupleTableSlot *slot, int natts)
        {
                /* Restore state from previous execution */
                off = slot->tts_off;
-               slow = slot->tts_slow;
+               slow = TTS_SLOW(slot);
        }
 
        tp = (char *) tup + tup->t_hoff;
@@ -1452,7 +1452,10 @@ slot_deform_tuple(TupleTableSlot *slot, int natts)
         */
        slot->tts_nvalid = attnum;
        slot->tts_off = off;
-       slot->tts_slow = slow;
+       if (slow)
+               slot->tts_flags |= TTS_FLAG_SLOW;
+       else
+               slot->tts_flags &= ~TTS_FLAG_SLOW;
 }
 
 /*
index fbbac1c6bf81f7095c40cbcdc4ee3f91bf656484..015752a93f18d224489badf6ff33ef451fea4791 100644 (file)
@@ -101,11 +101,10 @@ MakeTupleTableSlot(TupleDesc tupleDesc)
 
        slot = palloc0(sz);
        slot->type = T_TupleTableSlot;
-       slot->tts_isempty = true;
-       slot->tts_shouldFree = false;
-       slot->tts_shouldFreeMin = false;
+       slot->tts_flags |= TTS_FLAG_EMPTY;
+       if (tupleDesc != NULL)
+               slot->tts_flags |= TTS_FLAG_FIXED;
        slot->tts_tuple = NULL;
-       slot->tts_fixedTupleDescriptor = tupleDesc != NULL;
        slot->tts_tupleDescriptor = tupleDesc;
        slot->tts_mcxt = CurrentMemoryContext;
        slot->tts_buffer = InvalidBuffer;
@@ -176,7 +175,7 @@ ExecResetTupleTable(List *tupleTable,       /* tuple table */
                /* If shouldFree, release memory occupied by the slot itself */
                if (shouldFree)
                {
-                       if (!slot->tts_fixedTupleDescriptor)
+                       if (!TTS_FIXED(slot))
                        {
                                if (slot->tts_values)
                                        pfree(slot->tts_values);
@@ -224,7 +223,7 @@ ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
        ExecClearTuple(slot);
        if (slot->tts_tupleDescriptor)
                ReleaseTupleDesc(slot->tts_tupleDescriptor);
-       if (!slot->tts_fixedTupleDescriptor)
+       if (!TTS_FIXED(slot))
        {
                if (slot->tts_values)
                        pfree(slot->tts_values);
@@ -254,7 +253,7 @@ void
 ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
                                          TupleDesc tupdesc)    /* new tuple descriptor */
 {
-       Assert(!slot->tts_fixedTupleDescriptor);
+       Assert(!TTS_FIXED(slot));
 
        /* For safety, make sure slot is empty before changing it */
        ExecClearTuple(slot);
@@ -325,17 +324,23 @@ ExecStoreHeapTuple(HeapTuple tuple,
        /*
         * Free any old physical tuple belonging to the slot.
         */
-       if (slot->tts_shouldFree)
+       if (TTS_SHOULDFREE(slot))
+       {
                heap_freetuple(slot->tts_tuple);
-       if (slot->tts_shouldFreeMin)
+               slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
+       }
+       if (TTS_SHOULDFREEMIN(slot))
+       {
                heap_free_minimal_tuple(slot->tts_mintuple);
+               slot->tts_flags &= ~TTS_FLAG_SHOULDFREEMIN;
+       }
 
        /*
         * Store the new tuple into the specified slot.
         */
-       slot->tts_isempty = false;
-       slot->tts_shouldFree = shouldFree;
-       slot->tts_shouldFreeMin = false;
+       slot->tts_flags &= ~TTS_FLAG_EMPTY;
+       if (shouldFree)
+               slot->tts_flags |= TTS_FLAG_SHOULDFREE;
        slot->tts_tuple = tuple;
        slot->tts_mintuple = NULL;
 
@@ -382,17 +387,21 @@ ExecStoreBufferHeapTuple(HeapTuple tuple,
        /*
         * Free any old physical tuple belonging to the slot.
         */
-       if (slot->tts_shouldFree)
+       if (TTS_SHOULDFREE(slot))
+       {
                heap_freetuple(slot->tts_tuple);
-       if (slot->tts_shouldFreeMin)
+               slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
+       }
+       if (TTS_SHOULDFREEMIN(slot))
+       {
                heap_free_minimal_tuple(slot->tts_mintuple);
+               slot->tts_flags &= ~TTS_FLAG_SHOULDFREEMIN;
+       }
 
        /*
         * Store the new tuple into the specified slot.
         */
-       slot->tts_isempty = false;
-       slot->tts_shouldFree = false;
-       slot->tts_shouldFreeMin = false;
+       slot->tts_flags &= ~TTS_FLAG_EMPTY;
        slot->tts_tuple = tuple;
        slot->tts_mintuple = NULL;
 
@@ -442,10 +451,16 @@ ExecStoreMinimalTuple(MinimalTuple mtup,
        /*
         * Free any old physical tuple belonging to the slot.
         */
-       if (slot->tts_shouldFree)
+       if (TTS_SHOULDFREE(slot))
+       {
                heap_freetuple(slot->tts_tuple);
-       if (slot->tts_shouldFreeMin)
+               slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
+       }
+       if (TTS_SHOULDFREEMIN(slot))
+       {
                heap_free_minimal_tuple(slot->tts_mintuple);
+               slot->tts_flags &= ~TTS_FLAG_SHOULDFREEMIN;
+       }
 
        /*
         * Drop the pin on the referenced buffer, if there is one.
@@ -458,9 +473,9 @@ ExecStoreMinimalTuple(MinimalTuple mtup,
        /*
         * Store the new tuple into the specified slot.
         */
-       slot->tts_isempty = false;
-       slot->tts_shouldFree = false;
-       slot->tts_shouldFreeMin = shouldFree;
+       slot->tts_flags &= ~TTS_FLAG_EMPTY;
+       if (shouldFree)
+               slot->tts_flags |= TTS_FLAG_SHOULDFREEMIN;
        slot->tts_tuple = &slot->tts_minhdr;
        slot->tts_mintuple = mtup;
 
@@ -493,15 +508,19 @@ ExecClearTuple(TupleTableSlot *slot)      /* slot in which to store tuple */
        /*
         * Free the old physical tuple if necessary.
         */
-       if (slot->tts_shouldFree)
+       if (TTS_SHOULDFREE(slot))
+       {
                heap_freetuple(slot->tts_tuple);
-       if (slot->tts_shouldFreeMin)
+               slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
+       }
+       if (TTS_SHOULDFREEMIN(slot))
+       {
                heap_free_minimal_tuple(slot->tts_mintuple);
+               slot->tts_flags &= ~TTS_FLAG_SHOULDFREEMIN;
+       }
 
        slot->tts_tuple = NULL;
        slot->tts_mintuple = NULL;
-       slot->tts_shouldFree = false;
-       slot->tts_shouldFreeMin = false;
 
        /*
         * Drop the pin on the referenced buffer, if there is one.
@@ -514,7 +533,7 @@ ExecClearTuple(TupleTableSlot *slot)        /* slot in which to store tuple */
        /*
         * Mark it empty.
         */
-       slot->tts_isempty = true;
+       slot->tts_flags |= TTS_FLAG_EMPTY;
        slot->tts_nvalid = 0;
 
        return slot;
@@ -539,9 +558,9 @@ ExecStoreVirtualTuple(TupleTableSlot *slot)
         */
        Assert(slot != NULL);
        Assert(slot->tts_tupleDescriptor != NULL);
-       Assert(slot->tts_isempty);
+       Assert(TTS_EMPTY(slot));
 
-       slot->tts_isempty = false;
+       slot->tts_flags &= ~TTS_FLAG_EMPTY;
        slot->tts_nvalid = slot->tts_tupleDescriptor->natts;
 
        return slot;
@@ -595,7 +614,7 @@ ExecCopySlotTuple(TupleTableSlot *slot)
         * sanity checks
         */
        Assert(slot != NULL);
-       Assert(!slot->tts_isempty);
+       Assert(!TTS_EMPTY(slot));
 
        /*
         * If we have a physical tuple (either format) then just copy it.
@@ -627,7 +646,7 @@ ExecCopySlotMinimalTuple(TupleTableSlot *slot)
         * sanity checks
         */
        Assert(slot != NULL);
-       Assert(!slot->tts_isempty);
+       Assert(!TTS_EMPTY(slot));
 
        /*
         * If we have a physical tuple then just copy it.  Prefer to copy
@@ -675,7 +694,7 @@ ExecFetchSlotTuple(TupleTableSlot *slot)
         * sanity checks
         */
        Assert(slot != NULL);
-       Assert(!slot->tts_isempty);
+       Assert(!TTS_EMPTY(slot));
 
        /*
         * If we have a regular physical tuple then just return it.
@@ -724,7 +743,8 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
         * sanity checks
         */
        Assert(slot != NULL);
-       Assert(!slot->tts_isempty);
+       Assert(!TTS_EMPTY(slot));
+
 
        /*
         * If we have a minimal physical tuple (local or not) then just return it.
@@ -741,7 +761,7 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
         */
        oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
        slot->tts_mintuple = ExecCopySlotMinimalTuple(slot);
-       slot->tts_shouldFreeMin = true;
+       slot->tts_flags |= TTS_FLAG_SHOULDFREEMIN;
        MemoryContextSwitchTo(oldContext);
 
        /*
@@ -797,13 +817,13 @@ ExecMaterializeSlot(TupleTableSlot *slot)
         * sanity checks
         */
        Assert(slot != NULL);
-       Assert(!slot->tts_isempty);
+       Assert(!TTS_EMPTY(slot));
 
        /*
         * If we have a regular physical tuple, and it's locally palloc'd, we have
         * nothing to do.
         */
-       if (slot->tts_tuple && slot->tts_shouldFree)
+       if (slot->tts_tuple && TTS_SHOULDFREE(slot))
                return slot->tts_tuple;
 
        /*
@@ -815,7 +835,7 @@ ExecMaterializeSlot(TupleTableSlot *slot)
         */
        oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
        slot->tts_tuple = ExecCopySlotTuple(slot);
-       slot->tts_shouldFree = true;
+       slot->tts_flags |= TTS_FLAG_SHOULDFREE;
        MemoryContextSwitchTo(oldContext);
 
        /*
@@ -842,7 +862,7 @@ ExecMaterializeSlot(TupleTableSlot *slot)
         * storage, we must not pfree it now, since callers might have already
         * fetched datum pointers referencing it.)
         */
-       if (!slot->tts_shouldFreeMin)
+       if (!TTS_SHOULDFREEMIN(slot))
                slot->tts_mintuple = NULL;
 
        return slot->tts_tuple;
index 98d8483b7201d626bc5f5118a7cb7ede2a6c8b99..2413f1f87db87d727bf51d15ce18a0d35ac1ed4d 100644 (file)
@@ -1080,7 +1080,7 @@ prepare_projection_slot(AggState *aggstate, TupleTableSlot *slot, int currentSet
 
                aggstate->grouped_cols = grouped_cols;
 
-               if (slot->tts_isempty)
+               if (TTS_EMPTY(slot))
                {
                        /*
                         * Force all values to be NULL if working on an empty input tuple
index 24beb40435e617b1aab7774615db36995f906f17..528f58717e2ccfbdf643e712f7757be9f5071a2c 100644 (file)
@@ -699,7 +699,7 @@ ExecDelete(ModifyTableState *mtstate,
                 * RETURNING expressions might reference the tableoid column, so
                 * initialize t_tableOid before evaluating them.
                 */
-               if (slot->tts_isempty)
+               if (TTS_EMPTY(slot))
                        ExecStoreAllNullTuple(slot);
                tuple = ExecMaterializeSlot(slot);
                tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
index 6d7ce21865c52dcfb478e08b2b2a2837bc305dc4..59e38d2d9557ce6ddfada883f98121f261f79ee7 100644 (file)
@@ -60,7 +60,7 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc, int natts)
        LLVMValueRef v_tts_values;
        LLVMValueRef v_tts_nulls;
        LLVMValueRef v_slotoffp;
-       LLVMValueRef v_slowp;
+       LLVMValueRef v_flagsp;
        LLVMValueRef v_nvalidp;
        LLVMValueRef v_nvalid;
        LLVMValueRef v_maxatt;
@@ -168,7 +168,7 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc, int natts)
                                                  "tts_ISNULL");
 
        v_slotoffp = LLVMBuildStructGEP(b, v_slot, FIELDNO_TUPLETABLESLOT_OFF, "");
-       v_slowp = LLVMBuildStructGEP(b, v_slot, FIELDNO_TUPLETABLESLOT_SLOW, "");
+       v_flagsp = LLVMBuildStructGEP(b, v_slot, FIELDNO_TUPLETABLESLOT_FLAGS, "");
        v_nvalidp = LLVMBuildStructGEP(b, v_slot, FIELDNO_TUPLETABLESLOT_NVALID, "");
 
        v_tupleheaderp =
@@ -690,11 +690,14 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc, int natts)
 
        {
                LLVMValueRef v_off = LLVMBuildLoad(b, v_offp, "");
+               LLVMValueRef v_flags;
 
                LLVMBuildStore(b, l_int16_const(natts), v_nvalidp);
                v_off = LLVMBuildTrunc(b, v_off, LLVMInt32Type(), "");
                LLVMBuildStore(b, v_off, v_slotoffp);
-               LLVMBuildStore(b, l_int8_const(1), v_slowp);
+               v_flags = LLVMBuildLoad(b, v_flagsp, "tts_flags");
+               v_flags = LLVMBuildOr(b, v_flags, l_int16_const(TTS_FLAG_SLOW), "");
+               LLVMBuildStore(b, v_flags, v_flagsp);
                LLVMBuildRetVoid(b);
        }
 
index 99e0cee157f8219539305c12f3440e956d6acf49..e5fe116acbc8cb1f3be7a3a688145f2e5f814a19 100644 (file)
@@ -292,7 +292,7 @@ llvm_compile_expr(ExprState *state)
                                                if (!desc &&
                                                        is &&
                                                        is->ps_ResultTupleSlot &&
-                                                       is->ps_ResultTupleSlot->tts_fixedTupleDescriptor)
+                                                       TTS_FIXED(is->ps_ResultTupleSlot))
                                                        desc = is->ps_ResultTupleSlot->tts_tupleDescriptor;
                                        }
                                        else if (opcode == EEOP_OUTER_FETCHSOME)
@@ -304,7 +304,7 @@ llvm_compile_expr(ExprState *state)
                                                if (!desc &&
                                                        os &&
                                                        os->ps_ResultTupleSlot &&
-                                                       os->ps_ResultTupleSlot->tts_fixedTupleDescriptor)
+                                                       TTS_FIXED(os->ps_ResultTupleSlot))
                                                        desc = os->ps_ResultTupleSlot->tts_tupleDescriptor;
                                        }
                                        else
index c9ed1988c5fb0759810586a7845bfe8be32dc616..edf538365be397c3dbb5a7d1a0f8430243d7ad82 100644 (file)
@@ -340,7 +340,7 @@ ExecProject(ProjectionInfo *projInfo)
         * Successfully formed a result row.  Mark the result slot as containing a
         * valid virtual tuple (inlined version of ExecStoreVirtualTuple()).
         */
-       slot->tts_isempty = false;
+       slot->tts_flags &= ~TTS_FLAG_EMPTY;
        slot->tts_nvalid = slot->tts_tupleDescriptor->natts;
 
        return slot;
index 3c8d57f37766a3c3ac9a3ba9c816d789fe43285e..b41b400ef186f7d9d4195a0d31848695d7d7e78e 100644 (file)
  * ie, only as needed.  This serves to avoid repeated extraction of data
  * from the physical tuple.
  *
- * A TupleTableSlot can also be "empty", holding no valid data.  This is
- * the only valid state for a freshly-created slot that has not yet had a
- * tuple descriptor assigned to it.  In this state, tts_isempty must be
- * true, tts_shouldFree false, tts_tuple NULL, tts_buffer InvalidBuffer,
- * and tts_nvalid zero.
+ * A TupleTableSlot can also be "empty", indicated by flag TTS_EMPTY set in
+ * tts_flags, holding no valid data.  This is the only valid state for a
+ * freshly-created slot that has not yet had a tuple descriptor assigned to it.
+ * In this state, TTS_SHOULDFREE should not be set in tts_flag, tts_tuple must
+ * be NULL, tts_buffer InvalidBuffer, and tts_nvalid zero.
  *
  * The tupleDescriptor is simply referenced, not copied, by the TupleTableSlot
  * code.  The caller of ExecSetSlotDescriptor() is responsible for providing
@@ -79,8 +79,9 @@
  * mechanism to do more.  However, the slot will increment the tupdesc
  * reference count if a reference-counted tupdesc is supplied.)
  *
- * When tts_shouldFree is true, the physical tuple is "owned" by the slot
- * and should be freed when the slot's reference to the tuple is dropped.
+ * When TTS_SHOULDFREE is set in tts_flags, the physical tuple is "owned" by
+ * the slot and should be freed when the slot's reference to the tuple is
+ * dropped.
  *
  * If tts_buffer is not InvalidBuffer, then the slot is holding a pin
  * on the indicated buffer page; drop the pin when we release the
  * MINIMAL_TUPLE_OFFSET bytes before tts_mintuple.  This allows column
  * extraction to treat the case identically to regular physical tuples.
  *
- * tts_slow/tts_off are saved state for slot_deform_tuple, and should not
- * be touched by any other code.
+ * TTS_SLOW flag in tts_flags and tts_off are saved state for
+ * slot_deform_tuple, and should not be touched by any other code.
  *----------
  */
+
+/* true = slot is empty */
+#define                        TTS_FLAG_EMPTY                  (1 << 1)
+#define TTS_EMPTY(slot)        (((slot)->tts_flags & TTS_FLAG_EMPTY) != 0)
+
+/* should pfree tts_tuple? */
+#define                        TTS_FLAG_SHOULDFREE             (1 << 2)
+#define TTS_SHOULDFREE(slot) (((slot)->tts_flags & TTS_FLAG_SHOULDFREE) != 0)
+
+/* should pfree tts_mintuple? */
+#define                        TTS_FLAG_SHOULDFREEMIN  (1 << 3)
+#define TTS_SHOULDFREEMIN(slot) (((slot)->tts_flags & TTS_FLAG_SHOULDFREEMIN) != 0)
+
+/* saved state for slot_deform_tuple */
+#define                        TTS_FLAG_SLOW           (1 << 4)
+#define TTS_SLOW(slot) (((slot)->tts_flags & TTS_FLAG_SLOW) != 0)
+
+/* fixed tuple descriptor */
+#define                        TTS_FLAG_FIXED          (1 << 5)
+#define TTS_FIXED(slot) (((slot)->tts_flags & TTS_FLAG_FIXED) != 0)
+
 typedef struct TupleTableSlot
 {
        NodeTag         type;
-       bool            tts_isempty;    /* true = slot is empty */
-       bool            tts_shouldFree; /* should pfree tts_tuple? */
-       bool            tts_shouldFreeMin;      /* should pfree tts_mintuple? */
-#define FIELDNO_TUPLETABLESLOT_SLOW 4
-       bool            tts_slow;               /* saved state for slot_deform_tuple */
-#define FIELDNO_TUPLETABLESLOT_TUPLE 5
+#define FIELDNO_TUPLETABLESLOT_FLAGS 1
+       uint16          tts_flags;              /* Boolean states */
+#define FIELDNO_TUPLETABLESLOT_NVALID 2
+       AttrNumber      tts_nvalid;             /* # of valid values in tts_values */
+#define FIELDNO_TUPLETABLESLOT_TUPLE 3
        HeapTuple       tts_tuple;              /* physical tuple, or NULL if virtual */
-#define FIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR 6
+#define FIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR 4
        TupleDesc       tts_tupleDescriptor;    /* slot's tuple descriptor */
        MemoryContext tts_mcxt;         /* slot itself is in this context */
        Buffer          tts_buffer;             /* tuple's buffer, or InvalidBuffer */
-#define FIELDNO_TUPLETABLESLOT_NVALID 9
-       AttrNumber      tts_nvalid;             /* # of valid values in tts_values */
-#define FIELDNO_TUPLETABLESLOT_VALUES 10
+#define FIELDNO_TUPLETABLESLOT_OFF 7
+       uint32          tts_off;                /* saved state for slot_deform_tuple */
+#define FIELDNO_TUPLETABLESLOT_VALUES 8
        Datum      *tts_values;         /* current per-attribute values */
-#define FIELDNO_TUPLETABLESLOT_ISNULL 11
+#define FIELDNO_TUPLETABLESLOT_ISNULL 9
        bool       *tts_isnull;         /* current per-attribute isnull flags */
        MinimalTuple tts_mintuple;      /* minimal tuple, or NULL if none */
        HeapTupleData tts_minhdr;       /* workspace for minimal-tuple-only case */
-#define FIELDNO_TUPLETABLESLOT_OFF 14
-       uint32          tts_off;                /* saved state for slot_deform_tuple */
-       bool            tts_fixedTupleDescriptor;       /* descriptor can't be changed */
 } TupleTableSlot;
 
 #define TTS_HAS_PHYSICAL_TUPLE(slot)  \
@@ -144,7 +162,7 @@ typedef struct TupleTableSlot
  * TupIsNull -- is a TupleTableSlot empty?
  */
 #define TupIsNull(slot) \
-       ((slot) == NULL || (slot)->tts_isempty)
+       ((slot) == NULL || TTS_EMPTY(slot))
 
 /* in executor/execTuples.c */
 extern TupleTableSlot *MakeTupleTableSlot(TupleDesc desc);