Compare commits

..

1 Commits

Author SHA1 Message Date
Andrew Kane
434ef7a5ab Updated SparsevecInnerProduct [skip ci] 2026-03-17 14:31:58 -07:00
13 changed files with 95 additions and 214 deletions

View File

@@ -11,8 +11,6 @@ Store your vectors with the rest of your data. Supports:
Plus [ACID](https://en.wikipedia.org/wiki/ACID) compliance, point-in-time recovery, JOINs, and all of the other [great features](https://www.postgresql.org/about/) of Postgres Plus [ACID](https://en.wikipedia.org/wiki/ACID) compliance, point-in-time recovery, JOINs, and all of the other [great features](https://www.postgresql.org/about/) of Postgres
Have a lot of vectors? Use [quantization](#scaling) to scale
[![Build Status](https://github.com/pgvector/pgvector/actions/workflows/build.yml/badge.svg)](https://github.com/pgvector/pgvector/actions) [![Build Status](https://github.com/pgvector/pgvector/actions/workflows/build.yml/badge.svg)](https://github.com/pgvector/pgvector/actions)
## Installation ## Installation
@@ -316,8 +314,6 @@ For a large number of workers, you may need to increase `max_parallel_workers` (
The [index options](#index-options) also have a significant impact on build time (use the defaults unless seeing low recall) The [index options](#index-options) also have a significant impact on build time (use the defaults unless seeing low recall)
Use [binary quantization](#binary-quantization) for faster build times at scale
### Indexing Progress ### Indexing Progress
Check [indexing progress](https://www.postgresql.org/docs/current/progress-reporting.html#CREATE-INDEX-PROGRESS-REPORTING) Check [indexing progress](https://www.postgresql.org/docs/current/progress-reporting.html#CREATE-INDEX-PROGRESS-REPORTING)
@@ -447,7 +443,13 @@ Exact indexes work well for conditions that match a low percentage of rows. Othe
CREATE INDEX ON items USING hnsw (embedding vector_l2_ops); CREATE INDEX ON items USING hnsw (embedding vector_l2_ops);
``` ```
With approximate indexes, filtering is applied *after* the index is scanned. If a condition matches 10% of rows, with HNSW and the default `hnsw.ef_search` of 40, only 4 rows will match on average. For more rows, enable [iterative index scans](#iterative-index-scans), which will automatically scan more of the index when needed. With approximate indexes, filtering is applied *after* the index is scanned. If a condition matches 10% of rows, with HNSW and the default `hnsw.ef_search` of 40, only 4 rows will match on average. For more rows, increase `hnsw.ef_search`.
```sql
SET hnsw.ef_search = 200;
```
Starting with 0.8.0, you can enable [iterative index scans](#iterative-index-scans), which will automatically scan more of the index when needed.
```sql ```sql
SET hnsw.iterative_scan = strict_order; SET hnsw.iterative_scan = strict_order;
@@ -671,10 +673,6 @@ SHOW shared_buffers;
Be sure to restart Postgres for changes to take effect. Be sure to restart Postgres for changes to take effect.
### Storing
Use the `halfvec` type instead of `vector` for a smaller working set.
### Loading ### Loading
Use `COPY` for bulk loading data ([example](https://github.com/pgvector/pgvector-python/blob/master/examples/loading/example.py)). Use `COPY` for bulk loading data ([example](https://github.com/pgvector/pgvector-python/blob/master/examples/loading/example.py)).
@@ -689,8 +687,6 @@ Add any indexes *after* loading the initial data for best performance.
See index build time for [HNSW](#index-build-time) and [IVFFlat](#index-build-time-1). See index build time for [HNSW](#index-build-time) and [IVFFlat](#index-build-time-1).
Use [binary quantization](#binary-quantization) for smaller indexes and faster build times at scale.
In production environments, create indexes concurrently to avoid blocking writes. In production environments, create indexes concurrently to avoid blocking writes.
```sql ```sql
@@ -721,8 +717,6 @@ SELECT * FROM items ORDER BY embedding <#> '[3,1,2]' LIMIT 5;
#### Approximate Search #### Approximate Search
Use [binary quantization](#binary-quantization) with re-ranking to keep indexes in-memory at scale.
To speed up queries with an IVFFlat index, increase the number of inverted lists (at the expense of recall). To speed up queries with an IVFFlat index, increase the number of inverted lists (at the expense of recall).
```sql ```sql
@@ -738,20 +732,21 @@ REINDEX INDEX CONCURRENTLY index_name;
VACUUM table_name; VACUUM table_name;
``` ```
## Scaling
For a smaller working set:
1. Use the `halfvec` type instead of `vector` for tables
2. Use [binary quantization](#binary-quantization) for indexes (with re-ranking for search)
Scale vertically by increasing memory, CPU, and storage on a single instance. Use existing tools to [tune parameters](#tuning) and [monitor performance](#monitoring).
Scale horizontally with [replicas](https://www.postgresql.org/docs/current/hot-standby.html), or use [Citus](https://github.com/citusdata/citus), [PgDog](https://github.com/pgdogdev/pgdog), or another approach for sharding ([example](https://github.com/pgvector/pgvector-python/blob/master/examples/citus/example.py)).
## Monitoring ## Monitoring
Use existing tools like [pg_stat_statements](https://www.postgresql.org/docs/current/pgstatstatements.html) or [PgHero](https://github.com/ankane/pghero) to monitor performance. Monitor performance with [pg_stat_statements](https://www.postgresql.org/docs/current/pgstatstatements.html) (be sure to add it to `shared_preload_libraries`).
```sql
CREATE EXTENSION pg_stat_statements;
```
Get the most time-consuming queries with:
```sql
SELECT query, calls, ROUND((total_plan_time + total_exec_time) / calls) AS avg_time_ms,
ROUND((total_plan_time + total_exec_time) / 60000) AS total_time_min
FROM pg_stat_statements ORDER BY total_plan_time + total_exec_time DESC LIMIT 20;
```
Monitor recall by comparing results from approximate search with exact search. Monitor recall by comparing results from approximate search with exact search.
@@ -762,6 +757,14 @@ SELECT ...
COMMIT; COMMIT;
``` ```
## Scaling
Scale pgvector the same way you scale Postgres.
Scale vertically by increasing memory, CPU, and storage on a single instance. Use existing tools to [tune parameters](#tuning) and [monitor performance](#monitoring).
Scale horizontally with [replicas](https://www.postgresql.org/docs/current/hot-standby.html), or use [Citus](https://github.com/citusdata/citus) or another approach for sharding ([example](https://github.com/pgvector/pgvector-python/blob/master/examples/citus/example.py)).
## Languages ## Languages
Use pgvector from any language with a Postgres client. You can even generate and store vectors in one language and query them in another. Use pgvector from any language with a Postgres client. You can even generate and store vectors in one language and query them in another.
@@ -875,8 +878,6 @@ No, but like other index types, youll likely see better performance if they d
SELECT pg_size_pretty(pg_relation_size('index_name')); SELECT pg_size_pretty(pg_relation_size('index_name'));
``` ```
Use [half-precision indexing](#half-precision-indexing) or [binary quantization](#binary-quantization) for smaller indexes.
## Troubleshooting ## Troubleshooting
#### Why isnt a query using an index? #### Why isnt a query using an index?

View File

@@ -169,7 +169,7 @@ BitJaccardDistanceAvx512Popcount(uint32 bytes, unsigned char *ax, unsigned char
#endif #endif
TARGET_XSAVE static bool TARGET_XSAVE static bool
SupportsAvx512Popcount(void) SupportsAvx512Popcount()
{ {
unsigned int exx[4] = {0, 0, 0, 0}; unsigned int exx[4] = {0, 0, 0, 0};

View File

@@ -13,7 +13,6 @@
#include "hnsw.h" #include "hnsw.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "nodes/pg_list.h" #include "nodes/pg_list.h"
#include "storage/lwlock.h"
#include "utils/float.h" #include "utils/float.h"
#include "utils/guc.h" #include "utils/guc.h"
#include "utils/relcache.h" #include "utils/relcache.h"
@@ -258,11 +257,6 @@ hnswvalidate(Oid opclassoid)
return true; return true;
} }
static void
hnswunguardbatch(IndexScanDesc scan, IndexScanBatch batch)
{
}
/* /*
* Define index handler * Define index handler
* *
@@ -284,7 +278,6 @@ hnswhandler(PG_FUNCTION_ARGS)
.amconsistentequality = false, .amconsistentequality = false,
.amconsistentordering = false, .amconsistentordering = false,
.amcanbackward = false, .amcanbackward = false,
.amcanmarkpos = false,
.amcanunique = false, .amcanunique = false,
.amcanmulticol = false, .amcanmulticol = false,
.amoptionalkey = true, .amoptionalkey = true,
@@ -317,14 +310,11 @@ hnswhandler(PG_FUNCTION_ARGS)
.amadjustmembers = NULL, .amadjustmembers = NULL,
.ambeginscan = hnswbeginscan, .ambeginscan = hnswbeginscan,
.amrescan = hnswrescan, .amrescan = hnswrescan,
.amgettuple = NULL, .amgettuple = hnswgettuple,
.amgetbatch = hnswgetbatch,
.amunguardbatch = hnswunguardbatch,
.amkillitemsbatch = NULL,
.amgettransform = NULL,
.amgetbitmap = NULL, .amgetbitmap = NULL,
.amendscan = hnswendscan, .amendscan = hnswendscan,
.amposreset = NULL, .ammarkpos = NULL,
.amrestrpos = NULL,
.amestimateparallelscan = NULL, .amestimateparallelscan = NULL,
.aminitparallelscan = NULL, .aminitparallelscan = NULL,
.amparallelrescan = NULL, .amparallelrescan = NULL,

View File

@@ -10,10 +10,6 @@
#include "lib/pairingheap.h" #include "lib/pairingheap.h"
#include "nodes/execnodes.h" #include "nodes/execnodes.h"
#include "port.h" /* for random() */ #include "port.h" /* for random() */
#include "storage/bufpage.h"
#include "storage/condition_variable.h"
#include "storage/lwlock.h"
#include "storage/s_lock.h"
#include "utils/relptr.h" #include "utils/relptr.h"
#include "utils/sampling.h" #include "utils/sampling.h"
#include "vector.h" #include "vector.h"
@@ -465,7 +461,7 @@ IndexBulkDeleteResult *hnswbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResu
IndexBulkDeleteResult *hnswvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats); IndexBulkDeleteResult *hnswvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats);
IndexScanDesc hnswbeginscan(Relation index, int nkeys, int norderbys); IndexScanDesc hnswbeginscan(Relation index, int nkeys, int norderbys);
void hnswrescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int norderbys); void hnswrescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int norderbys);
IndexScanBatch hnswgetbatch(IndexScanDesc scan, IndexScanBatch priorbatch, ScanDirection dir); bool hnswgettuple(IndexScanDesc scan, ScanDirection dir);
void hnswendscan(IndexScanDesc scan); void hnswendscan(IndexScanDesc scan);
static inline HnswNeighborArray * static inline HnswNeighborArray *

View File

@@ -54,7 +54,6 @@
#include "nodes/execnodes.h" #include "nodes/execnodes.h"
#include "optimizer/optimizer.h" #include "optimizer/optimizer.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/condition_variable.h"
#include "tcop/tcopprot.h" #include "tcop/tcopprot.h"
#include "utils/datum.h" #include "utils/datum.h"
#include "utils/memutils.h" #include "utils/memutils.h"
@@ -803,11 +802,7 @@ HnswParallelScanAndInsert(Relation heapRel, Relation indexRel, HnswShared * hnsw
buildstate.hnswarea = hnswarea; buildstate.hnswarea = hnswarea;
InitAllocator(&buildstate.allocator, &HnswSharedMemoryAlloc, &buildstate); InitAllocator(&buildstate.allocator, &HnswSharedMemoryAlloc, &buildstate);
scan = table_beginscan_parallel(heapRel, scan = table_beginscan_parallel(heapRel,
ParallelTableScanFromHnswShared(hnswshared) ParallelTableScanFromHnswShared(hnswshared));
#if PG_VERSION_NUM >= 190000
,SO_NONE
#endif
);
reltuples = table_index_build_scan(heapRel, indexRel, indexInfo, reltuples = table_index_build_scan(heapRel, indexRel, indexInfo,
true, progress, BuildCallback, true, progress, BuildCallback,
(void *) &buildstate, scan); (void *) &buildstate, scan);

View File

@@ -6,7 +6,6 @@
#include "nodes/execnodes.h" #include "nodes/execnodes.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
#include "storage/lwlock.h"
#include "utils/datum.h" #include "utils/datum.h"
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/rel.h" #include "utils/rel.h"

View File

@@ -3,7 +3,6 @@
#include <limits.h> #include <limits.h>
#include "access/genam.h" #include "access/genam.h"
#include "access/indexbatch.h"
#include "access/relscan.h" #include "access/relscan.h"
#include "hnsw.h" #include "hnsw.h"
#include "lib/pairingheap.h" #include "lib/pairingheap.h"
@@ -136,11 +135,6 @@ hnswbeginscan(Relation index, int nkeys, int norderbys)
double maxMemory; double maxMemory;
scan = RelationGetIndexScan(index, nkeys, norderbys); scan = RelationGetIndexScan(index, nkeys, norderbys);
scan->maxitemsbatch = hnsw_ef_search * HNSW_HEAPTIDS;
/* unused but must be > 0 */
scan->batch_index_opaque_static = MAXALIGN(1);
scan->batch_index_opaque_dyn = 0;
scan->batch_tuples_workspace = 0;
so = (HnswScanOpaque) palloc(sizeof(HnswScanOpaqueData)); so = (HnswScanOpaque) palloc(sizeof(HnswScanOpaqueData));
so->typeInfo = HnswGetTypeInfo(index); so->typeInfo = HnswGetTypeInfo(index);
@@ -190,13 +184,12 @@ hnswrescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int no
} }
/* /*
* Fetch the next batch in the given scan * Fetch the next tuple in the given scan
*/ */
IndexScanBatch bool
hnswgetbatch(IndexScanDesc scan, IndexScanBatch priorbatch, ScanDirection dir) hnswgettuple(IndexScanDesc scan, ScanDirection dir)
{ {
HnswScanOpaque so = (HnswScanOpaque) scan->opaque; HnswScanOpaque so = (HnswScanOpaque) scan->opaque;
IndexScanBatch batch = indexam_util_alloc_batch(scan);
MemoryContext oldCtx = MemoryContextSwitchTo(so->tmpCtx); MemoryContext oldCtx = MemoryContextSwitchTo(so->tmpCtx);
/* /*
@@ -252,7 +245,6 @@ hnswgetbatch(IndexScanDesc scan, IndexScanBatch priorbatch, ScanDirection dir)
HnswSearchCandidate *sc; HnswSearchCandidate *sc;
HnswElement element; HnswElement element;
ItemPointer heaptid; ItemPointer heaptid;
int nitems = 0;
if (list_length(so->w) == 0) if (list_length(so->w) == 0)
{ {
@@ -298,8 +290,6 @@ hnswgetbatch(IndexScanDesc scan, IndexScanBatch priorbatch, ScanDirection dir)
break; break;
} }
while (list_length(so->w) != 0)
{
sc = llast(so->w); sc = llast(so->w);
element = HnswPtrAccess(base, sc->element); element = HnswPtrAccess(base, sc->element);
@@ -328,34 +318,16 @@ hnswgetbatch(IndexScanDesc scan, IndexScanBatch priorbatch, ScanDirection dir)
so->previousDistance = sc->distance; so->previousDistance = sc->distance;
} }
batch->items[nitems].tableTid = *heaptid;
batch->items[nitems].indexOffset = -1;
batch->items[nitems].tupleOffset = 0;
nitems++;
/* Keep batch size flexible */
if (nitems == scan->maxitemsbatch)
break;
}
/* Needed for strict iterative scans */
if (nitems == 0)
continue;
MemoryContextSwitchTo(oldCtx); MemoryContextSwitchTo(oldCtx);
scan->xs_heaptid = *heaptid;
scan->xs_recheck = false; scan->xs_recheck = false;
scan->xs_recheckorderby = false; scan->xs_recheckorderby = false;
return true;
batch->firstItem = 0;
batch->lastItem = nitems - 1;
batch->dir = ForwardScanDirection;
return batch;
} }
MemoryContextSwitchTo(oldCtx); MemoryContextSwitchTo(oldCtx);
indexam_util_release_batch(scan, batch); return false;
return NULL;
} }
/* /*

View File

@@ -22,7 +22,6 @@
#include "nodes/execnodes.h" #include "nodes/execnodes.h"
#include "optimizer/optimizer.h" #include "optimizer/optimizer.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/condition_variable.h"
#include "tcop/tcopprot.h" #include "tcop/tcopprot.h"
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/rel.h" #include "utils/rel.h"
@@ -63,13 +62,15 @@ AddSample(Datum *values, IvfflatBuildState * buildstate)
Datum value = PointerGetDatum(PG_DETOAST_DATUM(values[0])); Datum value = PointerGetDatum(PG_DETOAST_DATUM(values[0]));
/* /*
* Check with KMEANS_NORM_PROC that the value can be normalized since * Normalize with KMEANS_NORM_PROC since spherical distance function
* spherical distance function expects unit vectors * expects unit vectors
*/ */
if (buildstate->kmeansnormprocinfo != NULL) if (buildstate->kmeansnormprocinfo != NULL)
{ {
if (!IvfflatCheckNorm(buildstate->kmeansnormprocinfo, buildstate->collation, value)) if (!IvfflatCheckNorm(buildstate->kmeansnormprocinfo, buildstate->collation, value))
return; return;
value = IvfflatNormValue(buildstate->typeInfo, buildstate->collation, value);
} }
if (samples->length < targsamples) if (samples->length < targsamples)
@@ -80,7 +81,7 @@ AddSample(Datum *values, IvfflatBuildState * buildstate)
else else
{ {
if (buildstate->rowstoskip < 0) if (buildstate->rowstoskip < 0)
buildstate->rowstoskip = reservoir_get_next_S(&buildstate->rstate, buildstate->samplerows, targsamples); buildstate->rowstoskip = reservoir_get_next_S(&buildstate->rstate, samples->length, targsamples);
if (buildstate->rowstoskip <= 0) if (buildstate->rowstoskip <= 0)
{ {
@@ -96,9 +97,6 @@ AddSample(Datum *values, IvfflatBuildState * buildstate)
buildstate->rowstoskip -= 1; buildstate->rowstoskip -= 1;
} }
/* Increment after reservoir_get_next_S */
buildstate->samplerows += 1;
} }
/* /*
@@ -135,7 +133,6 @@ SampleRows(IvfflatBuildState * buildstate)
int targsamples = buildstate->samples->maxlen; int targsamples = buildstate->samples->maxlen;
BlockNumber totalblocks = RelationGetNumberOfBlocks(buildstate->heap); BlockNumber totalblocks = RelationGetNumberOfBlocks(buildstate->heap);
buildstate->samplerows = 0;
buildstate->rowstoskip = -1; buildstate->rowstoskip = -1;
BlockSampler_Init(&buildstate->bs, totalblocks, targsamples, RandomInt()); BlockSampler_Init(&buildstate->bs, totalblocks, targsamples, RandomInt());
@@ -145,24 +142,8 @@ SampleRows(IvfflatBuildState * buildstate)
{ {
BlockNumber targblock = BlockSampler_Next(&buildstate->bs); BlockNumber targblock = BlockSampler_Next(&buildstate->bs);
/* Set anyvisible to false like table_index_build_scan */
table_index_build_range_scan(buildstate->heap, buildstate->index, buildstate->indexInfo, table_index_build_range_scan(buildstate->heap, buildstate->index, buildstate->indexInfo,
false, false, false, targblock, 1, SampleCallback, (void *) buildstate, NULL); false, true, false, targblock, 1, SampleCallback, (void *) buildstate, NULL);
}
/* Normalize if needed */
if (buildstate->kmeansnormprocinfo != NULL)
{
VectorArray samples = buildstate->samples;
for (int i = 0; i < samples->length; i++)
{
Datum value = PointerGetDatum(VectorArrayGet(samples, i));
Datum normValue = IvfflatNormValue(buildstate->typeInfo, buildstate->collation, value);
VectorArraySet(samples, i, DatumGetPointer(normValue));
pfree(DatumGetPointer(normValue));
}
} }
} }
@@ -393,9 +374,6 @@ InitBuildState(IvfflatBuildState * buildstate, Relation heap, Relation index, In
TupleDescInitEntry(buildstate->sortdesc, (AttrNumber) 1, "list", INT4OID, -1, 0); TupleDescInitEntry(buildstate->sortdesc, (AttrNumber) 1, "list", INT4OID, -1, 0);
TupleDescInitEntry(buildstate->sortdesc, (AttrNumber) 2, "tid", TIDOID, -1, 0); TupleDescInitEntry(buildstate->sortdesc, (AttrNumber) 2, "tid", TIDOID, -1, 0);
TupleDescInitEntry(buildstate->sortdesc, (AttrNumber) 3, "vector", TupleDescAttr(buildstate->tupdesc, 0)->atttypid, -1, 0); TupleDescInitEntry(buildstate->sortdesc, (AttrNumber) 3, "vector", TupleDescAttr(buildstate->tupdesc, 0)->atttypid, -1, 0);
#if PG_VERSION_NUM >= 190000
TupleDescFinalize(buildstate->sortdesc);
#endif
buildstate->slot = MakeSingleTupleTableSlot(buildstate->sortdesc, &TTSOpsVirtual); buildstate->slot = MakeSingleTupleTableSlot(buildstate->sortdesc, &TTSOpsVirtual);
@@ -457,7 +435,7 @@ ComputeCenters(IvfflatBuildState * buildstate)
buildstate->samples = VectorArrayInit(numSamples, buildstate->dimensions, buildstate->centers->itemsize); buildstate->samples = VectorArrayInit(numSamples, buildstate->dimensions, buildstate->centers->itemsize);
if (buildstate->heap != NULL) if (buildstate->heap != NULL)
{ {
IvfflatBench("sample rows", SampleRows(buildstate)); SampleRows(buildstate);
if (buildstate->samples->length < buildstate->lists) if (buildstate->samples->length < buildstate->lists)
{ {
@@ -672,11 +650,7 @@ IvfflatParallelScanAndSort(IvfflatSpool * ivfspool, IvfflatShared * ivfshared, S
ivfspool->sortstate = InitBuildSortState(buildstate.sortdesc, sortmem, coordinate); ivfspool->sortstate = InitBuildSortState(buildstate.sortdesc, sortmem, coordinate);
buildstate.sortstate = ivfspool->sortstate; buildstate.sortstate = ivfspool->sortstate;
scan = table_beginscan_parallel(ivfspool->heap, scan = table_beginscan_parallel(ivfspool->heap,
ParallelTableScanFromIvfflatShared(ivfshared) ParallelTableScanFromIvfflatShared(ivfshared));
#if PG_VERSION_NUM >= 190000
,SO_NONE
#endif
);
reltuples = table_index_build_scan(ivfspool->heap, ivfspool->index, indexInfo, reltuples = table_index_build_scan(ivfspool->heap, ivfspool->index, indexInfo,
true, progress, BuildCallback, true, progress, BuildCallback,
(void *) &buildstate, scan); (void *) &buildstate, scan);

View File

@@ -175,11 +175,6 @@ ivfflatvalidate(Oid opclassoid)
return true; return true;
} }
static void
ivfflatunguardbatch(IndexScanDesc scan, IndexScanBatch batch)
{
}
/* /*
* Define index handler * Define index handler
* *
@@ -201,7 +196,6 @@ ivfflathandler(PG_FUNCTION_ARGS)
.amconsistentequality = false, .amconsistentequality = false,
.amconsistentordering = false, .amconsistentordering = false,
.amcanbackward = false, .amcanbackward = false,
.amcanmarkpos = false,
.amcanunique = false, .amcanunique = false,
.amcanmulticol = false, .amcanmulticol = false,
.amoptionalkey = true, .amoptionalkey = true,
@@ -234,14 +228,11 @@ ivfflathandler(PG_FUNCTION_ARGS)
.amadjustmembers = NULL, .amadjustmembers = NULL,
.ambeginscan = ivfflatbeginscan, .ambeginscan = ivfflatbeginscan,
.amrescan = ivfflatrescan, .amrescan = ivfflatrescan,
.amgettuple = NULL, .amgettuple = ivfflatgettuple,
.amgetbatch = ivfflatgetbatch,
.amunguardbatch = ivfflatunguardbatch,
.amkillitemsbatch = NULL,
.amgettransform = NULL,
.amgetbitmap = NULL, .amgetbitmap = NULL,
.amendscan = ivfflatendscan, .amendscan = ivfflatendscan,
.amposreset = NULL, .ammarkpos = NULL,
.amrestrpos = NULL,
.amestimateparallelscan = NULL, .amestimateparallelscan = NULL,
.aminitparallelscan = NULL, .aminitparallelscan = NULL,
.amparallelrescan = NULL, .amparallelrescan = NULL,

View File

@@ -9,7 +9,6 @@
#include "lib/pairingheap.h" #include "lib/pairingheap.h"
#include "nodes/execnodes.h" #include "nodes/execnodes.h"
#include "port.h" /* for random() */ #include "port.h" /* for random() */
#include "storage/condition_variable.h"
#include "utils/sampling.h" #include "utils/sampling.h"
#include "utils/tuplesort.h" #include "utils/tuplesort.h"
#include "vector.h" #include "vector.h"
@@ -214,8 +213,7 @@ typedef struct IvfflatBuildState
/* Sampling */ /* Sampling */
BlockSamplerData bs; BlockSamplerData bs;
ReservoirStateData rstate; ReservoirStateData rstate;
double samplerows; int rowstoskip;
double rowstoskip;
/* Sorting */ /* Sorting */
Tuplesortstate *sortstate; Tuplesortstate *sortstate;
@@ -344,7 +342,7 @@ IndexBulkDeleteResult *ivfflatbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteR
IndexBulkDeleteResult *ivfflatvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats); IndexBulkDeleteResult *ivfflatvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats);
IndexScanDesc ivfflatbeginscan(Relation index, int nkeys, int norderbys); IndexScanDesc ivfflatbeginscan(Relation index, int nkeys, int norderbys);
void ivfflatrescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int norderbys); void ivfflatrescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int norderbys);
IndexScanBatch ivfflatgetbatch(IndexScanDesc scan, IndexScanBatch priorbatch, ScanDirection dir); bool ivfflatgettuple(IndexScanDesc scan, ScanDirection dir);
void ivfflatendscan(IndexScanDesc scan); void ivfflatendscan(IndexScanDesc scan);
#endif #endif

View File

@@ -3,7 +3,6 @@
#include <float.h> #include <float.h>
#include "access/genam.h" #include "access/genam.h"
#include "access/indexbatch.h"
#include "access/itup.h" #include "access/itup.h"
#include "access/relscan.h" #include "access/relscan.h"
#include "access/tupdesc.h" #include "access/tupdesc.h"
@@ -262,11 +261,6 @@ ivfflatbeginscan(Relation index, int nkeys, int norderbys)
MemoryContext oldCtx; MemoryContext oldCtx;
scan = RelationGetIndexScan(index, nkeys, norderbys); scan = RelationGetIndexScan(index, nkeys, norderbys);
scan->maxitemsbatch = 1000;
/* unused but must be > 0 */
scan->batch_index_opaque_static = MAXALIGN(1);
scan->batch_index_opaque_dyn = 0;
scan->batch_tuples_workspace = 0;
/* Get lists and dimensions from metapage */ /* Get lists and dimensions from metapage */
IvfflatGetMetaPageInfo(index, &lists, &dimensions); IvfflatGetMetaPageInfo(index, &lists, &dimensions);
@@ -304,9 +298,6 @@ ivfflatbeginscan(Relation index, int nkeys, int norderbys)
so->tupdesc = CreateTemplateTupleDesc(2); so->tupdesc = CreateTemplateTupleDesc(2);
TupleDescInitEntry(so->tupdesc, (AttrNumber) 1, "distance", FLOAT8OID, -1, 0); TupleDescInitEntry(so->tupdesc, (AttrNumber) 1, "distance", FLOAT8OID, -1, 0);
TupleDescInitEntry(so->tupdesc, (AttrNumber) 2, "heaptid", TIDOID, -1, 0); TupleDescInitEntry(so->tupdesc, (AttrNumber) 2, "heaptid", TIDOID, -1, 0);
#if PG_VERSION_NUM >= 190000
TupleDescFinalize(so->tupdesc);
#endif
/* Prep sort */ /* Prep sort */
so->sortstate = InitScanSortState(so->tupdesc); so->sortstate = InitScanSortState(so->tupdesc);
@@ -354,16 +345,14 @@ ivfflatrescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int
} }
/* /*
* Fetch the next batch in the given scan * Fetch the next tuple in the given scan
*/ */
IndexScanBatch bool
ivfflatgetbatch(IndexScanDesc scan, IndexScanBatch priorbatch, ScanDirection dir) ivfflatgettuple(IndexScanDesc scan, ScanDirection dir)
{ {
IvfflatScanOpaque so = (IvfflatScanOpaque) scan->opaque; IvfflatScanOpaque so = (IvfflatScanOpaque) scan->opaque;
IndexScanBatch batch = indexam_util_alloc_batch(scan);
ItemPointer heaptid; ItemPointer heaptid;
bool isnull; bool isnull;
int nitems = 0;
/* /*
* Index can be used to scan backward, but Postgres doesn't support * Index can be used to scan backward, but Postgres doesn't support
@@ -401,37 +390,17 @@ ivfflatgetbatch(IndexScanDesc scan, IndexScanBatch priorbatch, ScanDirection dir
while (!tuplesort_gettupleslot(so->sortstate, true, false, so->mslot, NULL)) while (!tuplesort_gettupleslot(so->sortstate, true, false, so->mslot, NULL))
{ {
if (so->listIndex == so->maxProbes) if (so->listIndex == so->maxProbes)
{ return false;
indexam_util_release_batch(scan, batch);
return NULL;
}
IvfflatBench("GetScanItems", GetScanItems(scan, so->value)); IvfflatBench("GetScanItems", GetScanItems(scan, so->value));
} }
for (;;)
{
heaptid = (ItemPointer) DatumGetPointer(slot_getattr(so->mslot, 2, &isnull)); heaptid = (ItemPointer) DatumGetPointer(slot_getattr(so->mslot, 2, &isnull));
batch->items[nitems].tableTid = *heaptid; scan->xs_heaptid = *heaptid;
batch->items[nitems].indexOffset = -1;
batch->items[nitems].tupleOffset = 0;
nitems++;
if (nitems == scan->maxitemsbatch)
break;
if (!tuplesort_gettupleslot(so->sortstate, true, false, so->mslot, NULL))
break;
}
scan->xs_recheck = false; scan->xs_recheck = false;
scan->xs_recheckorderby = false; scan->xs_recheckorderby = false;
return true;
batch->firstItem = 0;
batch->lastItem = nitems - 1;
batch->dir = ForwardScanDirection;
return batch;
} }
/* /*

View File

@@ -182,10 +182,10 @@ sparsevec_isspace(char ch)
static int static int
CompareIndices(const void *a, const void *b) CompareIndices(const void *a, const void *b)
{ {
if (((const SparseInputElement *) a)->index < ((const SparseInputElement *) b)->index) if (((SparseInputElement *) a)->index < ((SparseInputElement *) b)->index)
return -1; return -1;
if (((const SparseInputElement *) a)->index > ((const SparseInputElement *) b)->index) if (((SparseInputElement *) a)->index > ((SparseInputElement *) b)->index)
return 1; return 1;
return 0; return 0;
@@ -895,28 +895,24 @@ SparsevecInnerProduct(SparseVector * a, SparseVector * b)
float *ax = SPARSEVEC_VALUES(a); float *ax = SPARSEVEC_VALUES(a);
float *bx = SPARSEVEC_VALUES(b); float *bx = SPARSEVEC_VALUES(b);
float distance = 0.0; float distance = 0.0;
int bpos = 0; int i = 0;
int j = 0;
for (int i = 0; i < a->nnz; i++) while (i < a->nnz && j < b->nnz)
{ {
int ai = a->indices[i]; int ai = a->indices[i];
for (int j = bpos; j < b->nnz; j++)
{
int bi = b->indices[j]; int bi = b->indices[j];
/* Only update when the same index */
if (ai == bi) if (ai == bi)
{
distance += ax[i] * bx[j]; distance += ax[i] * bx[j];
i++;
/* Update start for next iteration */ j++;
if (ai >= bi)
bpos = j + 1;
/* Found or passed it */
if (bi >= ai)
break;
} }
else if (ai < bi)
i++;
else
j++;
} }
return distance; return distance;