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:
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user