1
0
mirror of https://github.com/pgvector/pgvector.git synced 2026-10-23 10:48:54 +03:00

Added free space map for HNSW [skip ci]

This commit is contained in:
Andrew Kane
2026-10-06 12:33:58 -07:00
parent f37c13f68b
commit 4da0270d86
4 changed files with 66 additions and 6 deletions

View File

@@ -485,6 +485,7 @@ void HnswSetElementTuple(char *base, HnswElementTuple etup, HnswElement element
void HnswUpdateConnection(char *base, HnswNeighborArray * neighbors, HnswElement newElement, float distance, int lm, int *updateIdx, Relation index, HnswSupport * support);
bool HnswLoadNeighborTids(HnswElement element, ItemPointerData *indextids, Relation index, int m, int lm, int lc);
void HnswInitLockTranche(void);
void HnswRecordPageWithFreeSpace(Relation index, BlockNumber blkno, Page page);
void HnswCheckDim(int expected, const HnswTypeInfo * typeInfo, Oid collation, Datum value);
const HnswTypeInfo *HnswGetTypeInfo(Relation index);
PGDLLEXPORT void HnswParallelBuildMain(dsm_segment *seg, shm_toc *toc);

View File

@@ -5,6 +5,7 @@
#include "hnsw.h"
#include "nodes/execnodes.h"
#include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/lmgr.h"
#include "storage/lwlock.h"
#include "utils/datum.h"
@@ -153,7 +154,7 @@ AddElementOnDisk(Relation index, HnswElement e, int m, BlockNumber insertPage, B
Size maxSize;
Size minCombinedSize;
HnswElementTuple etup;
BlockNumber currentPage = insertPage;
BlockNumber currentPage = InvalidBlockNumber;
HnswNeighborTuple ntup;
Buffer nbuf;
Page npage;
@@ -162,6 +163,8 @@ AddElementOnDisk(Relation index, HnswElement e, int m, BlockNumber insertPage, B
BlockNumber newInsertPage = InvalidBlockNumber;
uint8 tupleVersion;
char *base = NULL;
bool tryFsm = !building;
int fsmTries = 0;
/* Calculate sizes */
etupSize = HNSW_ELEMENT_TUPLE_SIZE(VARSIZE_ANY(HnswPtrAccess(base, e->value)));
@@ -181,6 +184,20 @@ AddElementOnDisk(Relation index, HnswElement e, int m, BlockNumber insertPage, B
/* Find a page (or two if needed) to insert the tuples */
for (;;)
{
bool fsmPage = false;
/* Try free space map */
if (tryFsm)
{
/* TODO do not retry same page */
currentPage = GetPageWithFreeSpace(index, etupSize);
fsmPage = BlockNumberIsValid(currentPage);
tryFsm = ++fsmTries < 3 && BlockNumberIsValid(currentPage);
}
if (!BlockNumberIsValid(currentPage))
currentPage = insertPage;
buf = ReadBuffer(index, currentPage);
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
@@ -196,7 +213,7 @@ AddElementOnDisk(Relation index, HnswElement e, int m, BlockNumber insertPage, B
}
/* Keep track of first page where element at level 0 can fit */
if (!BlockNumberIsValid(newInsertPage) && PageGetFreeSpace(page) >= minCombinedSize)
if (!BlockNumberIsValid(newInsertPage) && PageGetFreeSpace(page) >= minCombinedSize && !fsmPage)
newInsertPage = currentPage;
/* First, try the fastest path */
@@ -328,6 +345,10 @@ AddElementOnDisk(Relation index, HnswElement e, int m, BlockNumber insertPage, B
elog(ERROR, "failed to add index item to \"%s\"", RelationGetRelationName(index));
}
/* Update free space map */
if (!building)
HnswRecordPageWithFreeSpace(index, e->blkno, page);
/* Commit */
if (building)
{

View File

@@ -12,6 +12,7 @@
#include "port/atomics.h"
#include "sparsevec.h"
#include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "utils/datum.h"
#include "utils/fmgrprotos.h"
#include "utils/memdebug.h"
@@ -1359,6 +1360,39 @@ HnswFindElementNeighbors(char *base, HnswElement element, HnswElement entryPoint
}
}
/*
* Record free space
*/
void
HnswRecordPageWithFreeSpace(Relation index, BlockNumber blkno, Page page)
{
Size freeSpace = 0;
OffsetNumber maxoffno = PageGetMaxOffsetNumber(page);
for (OffsetNumber offno = FirstOffsetNumber; offno <= maxoffno; offno = OffsetNumberNext(offno))
{
ItemId itemid = PageGetItemId(page, offno);
HnswElementTuple etup = (HnswElementTuple) PageGetItem(page, itemid);
Size itemsize;
/* Skip neighbor tuples */
if (!HnswIsElementTuple(etup))
continue;
/* Skip live tuples */
if (!etup->deleted)
continue;
/* Keep track of largest element tuple that will fit */
itemsize = ItemIdGetLength(itemid);
freeSpace = Max(freeSpace, itemsize);
}
freeSpace += PageGetExactFreeSpace(page);
RecordPageWithFreeSpace(index, blkno, freeSpace);
}
/*
* Check dimensions match index
*/

View File

@@ -6,6 +6,7 @@
#include "hnsw.h"
#include "nodes/pg_list.h"
#include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/lmgr.h"
#include "utils/memutils.h"
#include "utils/rel.h"
@@ -709,21 +710,24 @@ MarkDeleted(HnswVacuumState * vacuumstate)
if (nbuf != buf)
UnlockReleaseBuffer(nbuf);
/* Set to first free page */
if (!BlockNumberIsValid(insertPage))
insertPage = blkno;
/* Prepare new xlog */
state = GenericXLogStart(index);
page = GenericXLogRegisterBuffer(state, buf, 0);
}
HnswRecordPageWithFreeSpace(index, blkno, page);
/* Set to last page */
insertPage = blkno;
blkno = HnswPageGetOpaque(page)->nextblkno;
GenericXLogAbort(state);
UnlockReleaseBuffer(buf);
}
FreeSpaceMapVacuum(index);
/* Update insert page last, after everything has been marked as deleted */
HnswUpdateMetaPage(index, 0, NULL, insertPage, MAIN_FORKNUM, false);
}