mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	Teach TID store to skip bitmap for small numbers of offsets
The header portion of BlocktableEntry has enough padding space for an array of 3 offsets (1 on 32-bit platforms). Use this space instead of having a sparse bitmap array. This will take up a constant amount of space no matter what the offsets are. Reviewed (in an earlier version) by Masahiko Sawada Discussion: https://postgr.es/m/CANWCAZYw+_KAaUNruhJfE=h6WgtBKeDG32St8vBJBEY82bGVRQ@mail.gmail.com Discussion: https://postgr.es/m/CAD21AoBci3Hujzijubomo1tdwH3XtQ9F89cTNQ4bsQijOmqnEw@mail.gmail.com
This commit is contained in:
		@@ -34,6 +34,9 @@
 | 
			
		||||
/* number of active words for a page: */
 | 
			
		||||
#define WORDS_PER_PAGE(n) ((n) / BITS_PER_BITMAPWORD + 1)
 | 
			
		||||
 | 
			
		||||
/* number of offsets we can store in the header of a BlocktableEntry */
 | 
			
		||||
#define NUM_FULL_OFFSETS ((sizeof(bitmapword) - sizeof(uint16)) / sizeof(OffsetNumber))
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This is named similarly to PagetableEntry in tidbitmap.c
 | 
			
		||||
 * because the two have a similar function.
 | 
			
		||||
@@ -41,6 +44,13 @@
 | 
			
		||||
typedef struct BlocktableEntry
 | 
			
		||||
{
 | 
			
		||||
	uint16		nwords;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * We can store a small number of offsets here to avoid wasting space with
 | 
			
		||||
	 * a sparse bitmap.
 | 
			
		||||
	 */
 | 
			
		||||
	OffsetNumber full_offsets[NUM_FULL_OFFSETS];
 | 
			
		||||
 | 
			
		||||
	bitmapword	words[FLEXIBLE_ARRAY_MEMBER];
 | 
			
		||||
} BlocktableEntry;
 | 
			
		||||
#define MaxBlocktableEntrySize \
 | 
			
		||||
@@ -331,6 +341,25 @@ TidStoreSetBlockOffsets(TidStore *ts, BlockNumber blkno, OffsetNumber *offsets,
 | 
			
		||||
	for (int i = 1; i < num_offsets; i++)
 | 
			
		||||
		Assert(offsets[i] > offsets[i - 1]);
 | 
			
		||||
 | 
			
		||||
	memset(page, 0, offsetof(BlocktableEntry, words));
 | 
			
		||||
 | 
			
		||||
	if (num_offsets <= NUM_FULL_OFFSETS)
 | 
			
		||||
	{
 | 
			
		||||
		for (int i = 0; i < num_offsets; i++)
 | 
			
		||||
		{
 | 
			
		||||
			OffsetNumber off = offsets[i];
 | 
			
		||||
 | 
			
		||||
			/* safety check to ensure we don't overrun bit array bounds */
 | 
			
		||||
			if (!OffsetNumberIsValid(off))
 | 
			
		||||
				elog(ERROR, "tuple offset out of range: %u", off);
 | 
			
		||||
 | 
			
		||||
			page->full_offsets[i] = off;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		page->nwords = 0;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		for (wordnum = 0, next_word_threshold = BITS_PER_BITMAPWORD;
 | 
			
		||||
			 wordnum <= WORDNUM(offsets[num_offsets - 1]);
 | 
			
		||||
			 wordnum++, next_word_threshold += BITS_PER_BITMAPWORD)
 | 
			
		||||
@@ -358,6 +387,7 @@ TidStoreSetBlockOffsets(TidStore *ts, BlockNumber blkno, OffsetNumber *offsets,
 | 
			
		||||
 | 
			
		||||
		page->nwords = wordnum;
 | 
			
		||||
		Assert(page->nwords == WORDS_PER_PAGE(offsets[num_offsets - 1]));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (TidStoreIsShared(ts))
 | 
			
		||||
		shared_ts_set(ts->tree.shared, blkno, page);
 | 
			
		||||
@@ -384,6 +414,18 @@ TidStoreIsMember(TidStore *ts, ItemPointer tid)
 | 
			
		||||
	if (page == NULL)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	if (page->nwords == 0)
 | 
			
		||||
	{
 | 
			
		||||
		/* we have offsets in the header */
 | 
			
		||||
		for (int i = 0; i < NUM_FULL_OFFSETS; i++)
 | 
			
		||||
		{
 | 
			
		||||
			if (page->full_offsets[i] == off)
 | 
			
		||||
				return true;
 | 
			
		||||
		}
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		wordnum = WORDNUM(off);
 | 
			
		||||
		bitnum = BITNUM(off);
 | 
			
		||||
 | 
			
		||||
@@ -392,6 +434,7 @@ TidStoreIsMember(TidStore *ts, ItemPointer tid)
 | 
			
		||||
			return false;
 | 
			
		||||
 | 
			
		||||
		return (page->words[wordnum] & ((bitmapword) 1 << bitnum)) != 0;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -511,6 +554,17 @@ tidstore_iter_extract_tids(TidStoreIter *iter, BlockNumber blkno,
 | 
			
		||||
	result->num_offsets = 0;
 | 
			
		||||
	result->blkno = blkno;
 | 
			
		||||
 | 
			
		||||
	if (page->nwords == 0)
 | 
			
		||||
	{
 | 
			
		||||
		/* we have offsets in the header */
 | 
			
		||||
		for (int i = 0; i < NUM_FULL_OFFSETS; i++)
 | 
			
		||||
		{
 | 
			
		||||
			if (page->full_offsets[i] != InvalidOffsetNumber)
 | 
			
		||||
				result->offsets[result->num_offsets++] = page->full_offsets[i];
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		for (wordnum = 0; wordnum < page->nwords; wordnum++)
 | 
			
		||||
		{
 | 
			
		||||
			bitmapword	w = page->words[wordnum];
 | 
			
		||||
@@ -532,4 +586,5 @@ tidstore_iter_extract_tids(TidStoreIter *iter, BlockNumber blkno,
 | 
			
		||||
				w >>= 1;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,20 @@ SELECT do_set_block_offsets(blk, array_agg(off)::int2[])
 | 
			
		||||
    (VALUES (0), (1), (:maxblkno / 2), (:maxblkno - 1), (:maxblkno)) AS blocks(blk),
 | 
			
		||||
    (VALUES (1), (2), (:maxoffset / 2), (:maxoffset - 1), (:maxoffset)) AS offsets(off)
 | 
			
		||||
  GROUP BY blk;
 | 
			
		||||
-- Test offsets embedded in the bitmap header.
 | 
			
		||||
SELECT do_set_block_offsets(501, array[greatest((random() * :maxoffset)::int, 1)]::int2[]);
 | 
			
		||||
 do_set_block_offsets 
 | 
			
		||||
----------------------
 | 
			
		||||
                  501
 | 
			
		||||
(1 row)
 | 
			
		||||
 | 
			
		||||
SELECT do_set_block_offsets(502, array_agg(DISTINCT greatest((random() * :maxoffset)::int, 1))::int2[])
 | 
			
		||||
  FROM generate_series(1, 3);
 | 
			
		||||
 do_set_block_offsets 
 | 
			
		||||
----------------------
 | 
			
		||||
                  502
 | 
			
		||||
(1 row)
 | 
			
		||||
 | 
			
		||||
-- Add enough TIDs to cause the store to appear "full", compared
 | 
			
		||||
-- to the allocated memory it started out with. This is easier
 | 
			
		||||
-- with memory contexts in local memory.
 | 
			
		||||
@@ -73,6 +87,20 @@ SELECT test_create(true);
 | 
			
		||||
 
 | 
			
		||||
(1 row)
 | 
			
		||||
 | 
			
		||||
-- Test offsets embedded in the bitmap header.
 | 
			
		||||
SELECT do_set_block_offsets(501, array[greatest((random() * :maxoffset)::int, 1)]::int2[]);
 | 
			
		||||
 do_set_block_offsets 
 | 
			
		||||
----------------------
 | 
			
		||||
                  501
 | 
			
		||||
(1 row)
 | 
			
		||||
 | 
			
		||||
SELECT do_set_block_offsets(502, array_agg(DISTINCT greatest((random() * :maxoffset)::int, 1))::int2[])
 | 
			
		||||
  FROM generate_series(1, 3);
 | 
			
		||||
 do_set_block_offsets 
 | 
			
		||||
----------------------
 | 
			
		||||
                  502
 | 
			
		||||
(1 row)
 | 
			
		||||
 | 
			
		||||
-- Random TIDs test. The offset numbers are randomized and must be
 | 
			
		||||
-- unique and ordered.
 | 
			
		||||
INSERT INTO hideblocks (blockno)
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,11 @@ SELECT do_set_block_offsets(blk, array_agg(off)::int2[])
 | 
			
		||||
    (VALUES (1), (2), (:maxoffset / 2), (:maxoffset - 1), (:maxoffset)) AS offsets(off)
 | 
			
		||||
  GROUP BY blk;
 | 
			
		||||
 | 
			
		||||
-- Test offsets embedded in the bitmap header.
 | 
			
		||||
SELECT do_set_block_offsets(501, array[greatest((random() * :maxoffset)::int, 1)]::int2[]);
 | 
			
		||||
SELECT do_set_block_offsets(502, array_agg(DISTINCT greatest((random() * :maxoffset)::int, 1))::int2[])
 | 
			
		||||
  FROM generate_series(1, 3);
 | 
			
		||||
 | 
			
		||||
-- Add enough TIDs to cause the store to appear "full", compared
 | 
			
		||||
-- to the allocated memory it started out with. This is easier
 | 
			
		||||
-- with memory contexts in local memory.
 | 
			
		||||
@@ -49,6 +54,11 @@ SELECT test_destroy();
 | 
			
		||||
-- because unused static functions would raise warnings there.
 | 
			
		||||
SELECT test_create(true);
 | 
			
		||||
 | 
			
		||||
-- Test offsets embedded in the bitmap header.
 | 
			
		||||
SELECT do_set_block_offsets(501, array[greatest((random() * :maxoffset)::int, 1)]::int2[]);
 | 
			
		||||
SELECT do_set_block_offsets(502, array_agg(DISTINCT greatest((random() * :maxoffset)::int, 1))::int2[])
 | 
			
		||||
  FROM generate_series(1, 3);
 | 
			
		||||
 | 
			
		||||
-- Random TIDs test. The offset numbers are randomized and must be
 | 
			
		||||
-- unique and ordered.
 | 
			
		||||
INSERT INTO hideblocks (blockno)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user