mirror of
https://github.com/pgvector/pgvector.git
synced 2026-07-22 12:07:34 +08:00
Compare commits
34 Commits
windows-ar
...
hnsw-read-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2f915c46b | ||
|
|
6caa4ec207 | ||
|
|
b9b5e8d305 | ||
|
|
1c8128d2f0 | ||
|
|
e09d9f0abb | ||
|
|
f6e9a56415 | ||
|
|
911935b695 | ||
|
|
44163d0a97 | ||
|
|
33ca8a61e2 | ||
|
|
742e2d1d28 | ||
|
|
a7c49d8283 | ||
|
|
ae9ee81e4d | ||
|
|
fa1dee4e3b | ||
|
|
e6bad96a03 | ||
|
|
3a49d141b3 | ||
|
|
ce09c9a27a | ||
|
|
870ca6724d | ||
|
|
8ad680f009 | ||
|
|
fe697e8788 | ||
|
|
bf28ed8176 | ||
|
|
799cfebf70 | ||
|
|
3cd1f09f66 | ||
|
|
e2efe62fe5 | ||
|
|
7b58352336 | ||
|
|
83d410eae9 | ||
|
|
ebbfe8dba0 | ||
|
|
e575866297 | ||
|
|
35f4f7fc80 | ||
|
|
32e95a8598 | ||
|
|
a03dc5b7d0 | ||
|
|
d365aac370 | ||
|
|
05182479a2 | ||
|
|
cc0958dec5 | ||
|
|
4af2b06dc5 |
@@ -1,8 +1,15 @@
|
|||||||
/.git/
|
/.git/
|
||||||
/dist/
|
/dist/
|
||||||
|
/log/
|
||||||
/results/
|
/results/
|
||||||
/tmp_check/
|
/tmp_check/
|
||||||
/sql/vector--?.?.?.sql
|
/sql/vector--?.?.?.sql
|
||||||
regression.*
|
regression.*
|
||||||
*.o
|
*.o
|
||||||
*.so
|
*.so
|
||||||
|
*.bc
|
||||||
|
*.dll
|
||||||
|
*.dylib
|
||||||
|
*.obj
|
||||||
|
*.lib
|
||||||
|
*.exp
|
||||||
|
|||||||
12
.github/workflows/build.yml
vendored
12
.github/workflows/build.yml
vendored
@@ -13,13 +13,13 @@ jobs:
|
|||||||
- postgres: 17
|
- postgres: 17
|
||||||
os: ubuntu-24.04
|
os: ubuntu-24.04
|
||||||
- postgres: 16
|
- postgres: 16
|
||||||
os: ubuntu-22.04
|
os: ubuntu-24.04-arm
|
||||||
- postgres: 15
|
- postgres: 15
|
||||||
os: ubuntu-22.04
|
os: ubuntu-22.04
|
||||||
- postgres: 14
|
- postgres: 14
|
||||||
os: ubuntu-20.04
|
os: ubuntu-22.04-arm
|
||||||
- postgres: 13
|
- postgres: 13
|
||||||
os: ubuntu-20.04
|
os: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: ankane/setup-postgres@v1
|
- uses: ankane/setup-postgres@v1
|
||||||
@@ -28,7 +28,7 @@ jobs:
|
|||||||
dev-files: true
|
dev-files: true
|
||||||
- run: make
|
- run: make
|
||||||
env:
|
env:
|
||||||
PG_CFLAGS: -DUSE_ASSERT_CHECKING -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare
|
PG_CFLAGS: ${{ matrix.postgres == 18 && '-Wno-missing-field-initializers' || '' }} -DUSE_ASSERT_CHECKING -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare
|
||||||
- run: |
|
- run: |
|
||||||
export PG_CONFIG=`which pg_config`
|
export PG_CONFIG=`which pg_config`
|
||||||
sudo --preserve-env=PG_CONFIG make install
|
sudo --preserve-env=PG_CONFIG make install
|
||||||
@@ -57,7 +57,7 @@ jobs:
|
|||||||
postgres-version: ${{ matrix.postgres }}
|
postgres-version: ${{ matrix.postgres }}
|
||||||
- run: make
|
- run: make
|
||||||
env:
|
env:
|
||||||
PG_CFLAGS: -DUSE_ASSERT_CHECKING -Wall -Wextra -Werror -Wno-unused-parameter
|
PG_CFLAGS: -DUSE_ASSERT_CHECKING -Wall -Wextra -Werror -Wno-unused-parameter -Wno-unknown-warning-option
|
||||||
- run: make install
|
- run: make install
|
||||||
- run: make installcheck
|
- run: make installcheck
|
||||||
- if: ${{ failure() }}
|
- if: ${{ failure() }}
|
||||||
@@ -87,7 +87,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
postgres-version: 14
|
postgres-version: 14
|
||||||
- run: |
|
- run: |
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars32.bat" && ^
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" && ^
|
||||||
nmake /NOLOGO /F Makefile.win && ^
|
nmake /NOLOGO /F Makefile.win && ^
|
||||||
nmake /NOLOGO /F Makefile.win install && ^
|
nmake /NOLOGO /F Makefile.win install && ^
|
||||||
nmake /NOLOGO /F Makefile.win installcheck && ^
|
nmake /NOLOGO /F Makefile.win installcheck && ^
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
## 0.8.1 (unreleased)
|
||||||
|
|
||||||
|
- Improved performance of HNSW index scans for Postgres 17
|
||||||
|
|
||||||
## 0.8.0 (2024-10-30)
|
## 0.8.0 (2024-10-30)
|
||||||
|
|
||||||
- Added support for iterative index scans
|
- Added support for iterative index scans
|
||||||
|
|||||||
30
README.md
30
README.md
@@ -33,18 +33,10 @@ You can also install it with [Docker](#docker), [Homebrew](#homebrew), [PGXN](#p
|
|||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
Ensure [C++ support in Visual Studio](https://learn.microsoft.com/en-us/cpp/build/building-on-the-command-line?view=msvc-170#download-and-install-the-tools) is installed, and run:
|
Ensure [C++ support in Visual Studio](https://learn.microsoft.com/en-us/cpp/build/building-on-the-command-line?view=msvc-170#download-and-install-the-tools) is installed and run `x64 Native Tools Command Prompt for VS [version]` as administrator. Then use `nmake` to build:
|
||||||
|
|
||||||
```cmd
|
```cmd
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat"
|
set "PGROOT=C:\Program Files\PostgreSQL\17"
|
||||||
```
|
|
||||||
|
|
||||||
Note: The exact path will vary depending on your Visual Studio version and edition
|
|
||||||
|
|
||||||
Then use `nmake` to build:
|
|
||||||
|
|
||||||
```cmd
|
|
||||||
set "PGROOT=C:\Program Files\PostgreSQL\16"
|
|
||||||
cd %TEMP%
|
cd %TEMP%
|
||||||
git clone --branch v0.8.0 https://github.com/pgvector/pgvector.git
|
git clone --branch v0.8.0 https://github.com/pgvector/pgvector.git
|
||||||
cd pgvector
|
cd pgvector
|
||||||
@@ -318,7 +310,9 @@ You can also speed up index creation by increasing the number of parallel worker
|
|||||||
SET max_parallel_maintenance_workers = 7; -- plus leader
|
SET max_parallel_maintenance_workers = 7; -- plus leader
|
||||||
```
|
```
|
||||||
|
|
||||||
For a large number of workers, you may also need to increase `max_parallel_workers` (8 by default)
|
For a large number of workers, you may need to increase `max_parallel_workers` (8 by default)
|
||||||
|
|
||||||
|
The [index options](#index-options) also have a significant impact on build time (use the defaults unless seeing low recall)
|
||||||
|
|
||||||
### Indexing Progress
|
### Indexing Progress
|
||||||
|
|
||||||
@@ -475,8 +469,6 @@ CREATE TABLE items (embedding vector(3), category_id int) PARTITION BY LIST(cate
|
|||||||
|
|
||||||
## Iterative Index Scans
|
## Iterative Index Scans
|
||||||
|
|
||||||
*Added in 0.8.0*
|
|
||||||
|
|
||||||
With approximate indexes, queries with filtering can return less results since filtering is applied *after* the index is scanned. Starting with 0.8.0, you can enable iterative index scans, which will automatically scan more of the index until enough results are found (or it reaches `hnsw.max_scan_tuples` or `ivfflat.max_probes`).
|
With approximate indexes, queries with filtering can return less results since filtering is applied *after* the index is scanned. Starting with 0.8.0, you can enable iterative index scans, which will automatically scan more of the index until enough results are found (or it reaches `hnsw.max_scan_tuples` or `ivfflat.max_probes`).
|
||||||
|
|
||||||
Iterative scans can use strict or relaxed ordering.
|
Iterative scans can use strict or relaxed ordering.
|
||||||
@@ -500,9 +492,11 @@ With relaxed ordering, you can use a [materialized CTE](https://www.postgresql.o
|
|||||||
```sql
|
```sql
|
||||||
WITH relaxed_results AS MATERIALIZED (
|
WITH relaxed_results AS MATERIALIZED (
|
||||||
SELECT id, embedding <-> '[1,2,3]' AS distance FROM items WHERE category_id = 123 ORDER BY distance LIMIT 5
|
SELECT id, embedding <-> '[1,2,3]' AS distance FROM items WHERE category_id = 123 ORDER BY distance LIMIT 5
|
||||||
) SELECT * FROM relaxed_results ORDER BY distance;
|
) SELECT * FROM relaxed_results ORDER BY distance + 0;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note: `+ 0` is needed for Postgres 17+
|
||||||
|
|
||||||
For queries that filter by distance, use a materialized CTE and place the distance filter outside of it for best performance (due to the [current behavior](https://www.postgresql.org/message-id/flat/CAOdR5yGUoMQ6j7M5hNUXrySzaqZVGf_Ne%2B8fwZMRKTFxU1nbJg%40mail.gmail.com) of the Postgres executor)
|
For queries that filter by distance, use a materialized CTE and place the distance filter outside of it for best performance (due to the [current behavior](https://www.postgresql.org/message-id/flat/CAOdR5yGUoMQ6j7M5hNUXrySzaqZVGf_Ne%2B8fwZMRKTFxU1nbJg%40mail.gmail.com) of the Postgres executor)
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
@@ -754,8 +748,6 @@ SELECT query, calls, ROUND((total_plan_time + total_exec_time) / calls) AS avg_t
|
|||||||
FROM pg_stat_statements ORDER BY total_plan_time + total_exec_time DESC LIMIT 20;
|
FROM pg_stat_statements ORDER BY total_plan_time + total_exec_time DESC LIMIT 20;
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: Replace `total_plan_time + total_exec_time` with `total_time` for Postgres < 13
|
|
||||||
|
|
||||||
Monitor recall by comparing results from approximate search with exact search.
|
Monitor recall by comparing results from approximate search with exact search.
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
@@ -793,7 +785,7 @@ Go | [pgvector-go](https://github.com/pgvector/pgvector-go)
|
|||||||
Haskell | [pgvector-haskell](https://github.com/pgvector/pgvector-haskell)
|
Haskell | [pgvector-haskell](https://github.com/pgvector/pgvector-haskell)
|
||||||
Java, Kotlin, Groovy, Scala | [pgvector-java](https://github.com/pgvector/pgvector-java)
|
Java, Kotlin, Groovy, Scala | [pgvector-java](https://github.com/pgvector/pgvector-java)
|
||||||
JavaScript, TypeScript | [pgvector-node](https://github.com/pgvector/pgvector-node)
|
JavaScript, TypeScript | [pgvector-node](https://github.com/pgvector/pgvector-node)
|
||||||
Julia | [pgvector-julia](https://github.com/pgvector/pgvector-julia)
|
Julia | [Pgvector.jl](https://github.com/pgvector/Pgvector.jl)
|
||||||
Lisp | [pgvector-lisp](https://github.com/pgvector/pgvector-lisp)
|
Lisp | [pgvector-lisp](https://github.com/pgvector/pgvector-lisp)
|
||||||
Lua | [pgvector-lua](https://github.com/pgvector/pgvector-lua)
|
Lua | [pgvector-lua](https://github.com/pgvector/pgvector-lua)
|
||||||
Nim | [pgvector-nim](https://github.com/pgvector/pgvector-nim)
|
Nim | [pgvector-nim](https://github.com/pgvector/pgvector-nim)
|
||||||
@@ -824,7 +816,7 @@ You can use [half-precision indexing](#half-precision-indexing) to index up to 4
|
|||||||
|
|
||||||
#### Can I store vectors with different dimensions in the same column?
|
#### Can I store vectors with different dimensions in the same column?
|
||||||
|
|
||||||
You can use `vector` as the type (instead of `vector(3)`).
|
You can use `vector` as the type (instead of `vector(n)`).
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
CREATE TABLE embeddings (model_id bigint, item_id bigint, embedding vector, PRIMARY KEY (model_id, item_id));
|
CREATE TABLE embeddings (model_id bigint, item_id bigint, embedding vector, PRIMARY KEY (model_id, item_id));
|
||||||
@@ -1129,7 +1121,7 @@ If compilation fails with `Cannot open include file: 'postgres.h': No such file
|
|||||||
|
|
||||||
### Mismatched Architecture
|
### Mismatched Architecture
|
||||||
|
|
||||||
If compilation fails with `error C2196: case value '4' already used`, make sure `vcvars64.bat` was called.
|
If compilation fails with `error C2196: case value '4' already used`, make sure you’re using the `x64 Native Tools Command Prompt`. Then run `nmake /F Makefile.win clean` and re-run the installation instructions.
|
||||||
|
|
||||||
### Missing Symbol
|
### Missing Symbol
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,6 @@
|
|||||||
#error "Requires PostgreSQL 13+"
|
#error "Requires PostgreSQL 13+"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Check architecture in first header */
|
|
||||||
StaticAssertDecl(sizeof(Datum) == SIZEOF_DATUM, "Architecture mismatch");
|
|
||||||
|
|
||||||
extern uint64 (*BitHammingDistance) (uint32 bytes, unsigned char *ax, unsigned char *bx, uint64 distance);
|
extern uint64 (*BitHammingDistance) (uint32 bytes, unsigned char *ax, unsigned char *bx, uint64 distance);
|
||||||
extern double (*BitJaccardDistance) (uint32 bytes, unsigned char *ax, unsigned char *bx, uint64 ab, uint64 aa, uint64 bb);
|
extern double (*BitJaccardDistance) (uint32 bytes, unsigned char *ax, unsigned char *bx, uint64 ab, uint64 aa, uint64 bb);
|
||||||
|
|
||||||
|
|||||||
13
src/hnsw.c
13
src/hnsw.c
@@ -259,6 +259,11 @@ hnswhandler(PG_FUNCTION_ARGS)
|
|||||||
amroutine->amoptsprocnum = 0;
|
amroutine->amoptsprocnum = 0;
|
||||||
amroutine->amcanorder = false;
|
amroutine->amcanorder = false;
|
||||||
amroutine->amcanorderbyop = true;
|
amroutine->amcanorderbyop = true;
|
||||||
|
#if PG_VERSION_NUM >= 180000
|
||||||
|
amroutine->amcanhash = false;
|
||||||
|
amroutine->amconsistentequality = false;
|
||||||
|
amroutine->amconsistentordering = false;
|
||||||
|
#endif
|
||||||
amroutine->amcanbackward = false; /* can change direction mid-scan */
|
amroutine->amcanbackward = false; /* can change direction mid-scan */
|
||||||
amroutine->amcanunique = false;
|
amroutine->amcanunique = false;
|
||||||
amroutine->amcanmulticol = false;
|
amroutine->amcanmulticol = false;
|
||||||
@@ -291,6 +296,9 @@ hnswhandler(PG_FUNCTION_ARGS)
|
|||||||
amroutine->amvacuumcleanup = hnswvacuumcleanup;
|
amroutine->amvacuumcleanup = hnswvacuumcleanup;
|
||||||
amroutine->amcanreturn = NULL;
|
amroutine->amcanreturn = NULL;
|
||||||
amroutine->amcostestimate = hnswcostestimate;
|
amroutine->amcostestimate = hnswcostestimate;
|
||||||
|
#if PG_VERSION_NUM >= 180000
|
||||||
|
amroutine->amgettreeheight = NULL;
|
||||||
|
#endif
|
||||||
amroutine->amoptions = hnswoptions;
|
amroutine->amoptions = hnswoptions;
|
||||||
amroutine->amproperty = NULL; /* TODO AMPROP_DISTANCE_ORDERABLE */
|
amroutine->amproperty = NULL; /* TODO AMPROP_DISTANCE_ORDERABLE */
|
||||||
amroutine->ambuildphasename = hnswbuildphasename;
|
amroutine->ambuildphasename = hnswbuildphasename;
|
||||||
@@ -311,5 +319,10 @@ hnswhandler(PG_FUNCTION_ARGS)
|
|||||||
amroutine->aminitparallelscan = NULL;
|
amroutine->aminitparallelscan = NULL;
|
||||||
amroutine->amparallelrescan = NULL;
|
amroutine->amparallelrescan = NULL;
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM >= 180000
|
||||||
|
amroutine->amtranslatestrategy = NULL;
|
||||||
|
amroutine->amtranslatecmptype = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
PG_RETURN_POINTER(amroutine);
|
PG_RETURN_POINTER(amroutine);
|
||||||
}
|
}
|
||||||
|
|||||||
11
src/hnsw.h
11
src/hnsw.h
@@ -362,6 +362,13 @@ typedef union
|
|||||||
ItemPointerData indextid;
|
ItemPointerData indextid;
|
||||||
} HnswUnvisited;
|
} HnswUnvisited;
|
||||||
|
|
||||||
|
typedef struct HnswReadStreamData
|
||||||
|
{
|
||||||
|
HnswUnvisited *unvisited;
|
||||||
|
int unvisitedLength;
|
||||||
|
int visited;
|
||||||
|
} HnswReadStreamData;
|
||||||
|
|
||||||
typedef struct HnswScanOpaqueData
|
typedef struct HnswScanOpaqueData
|
||||||
{
|
{
|
||||||
const HnswTypeInfo *typeInfo;
|
const HnswTypeInfo *typeInfo;
|
||||||
@@ -417,13 +424,13 @@ bool HnswCheckNorm(HnswSupport * support, Datum value);
|
|||||||
Buffer HnswNewBuffer(Relation index, ForkNumber forkNum);
|
Buffer HnswNewBuffer(Relation index, ForkNumber forkNum);
|
||||||
void HnswInitPage(Buffer buf, Page page);
|
void HnswInitPage(Buffer buf, Page page);
|
||||||
void HnswInit(void);
|
void HnswInit(void);
|
||||||
List *HnswSearchLayer(char *base, HnswQuery * q, List *ep, int ef, int lc, Relation index, HnswSupport * support, int m, bool inserting, HnswElement skipElement, visited_hash * v, pairingheap **discarded, bool initVisited, int64 *tuples);
|
List *HnswSearchLayer(char *base, HnswQuery * q, List *ep, int ef, int lc, Relation index, HnswSupport * support, int m, bool inserting, HnswElement skipElement, visited_hash * v, pairingheap **discarded, bool initVisited, int64 *tuples, bool maintenance);
|
||||||
HnswElement HnswGetEntryPoint(Relation index);
|
HnswElement HnswGetEntryPoint(Relation index);
|
||||||
void HnswGetMetaPageInfo(Relation index, int *m, HnswElement * entryPoint);
|
void HnswGetMetaPageInfo(Relation index, int *m, HnswElement * entryPoint);
|
||||||
void *HnswAlloc(HnswAllocator * allocator, Size size);
|
void *HnswAlloc(HnswAllocator * allocator, Size size);
|
||||||
HnswElement HnswInitElement(char *base, ItemPointer tid, int m, double ml, int maxLevel, HnswAllocator * alloc);
|
HnswElement HnswInitElement(char *base, ItemPointer tid, int m, double ml, int maxLevel, HnswAllocator * alloc);
|
||||||
HnswElement HnswInitElementFromBlock(BlockNumber blkno, OffsetNumber offno);
|
HnswElement HnswInitElementFromBlock(BlockNumber blkno, OffsetNumber offno);
|
||||||
void HnswFindElementNeighbors(char *base, HnswElement element, HnswElement entryPoint, Relation index, HnswSupport * support, int m, int efConstruction, bool existing);
|
void HnswFindElementNeighbors(char *base, HnswElement element, HnswElement entryPoint, Relation index, HnswSupport * support, int m, int efConstruction, bool existing, bool maintenance);
|
||||||
HnswSearchCandidate *HnswEntryCandidate(char *base, HnswElement em, HnswQuery * q, Relation rel, HnswSupport * support, bool loadVec);
|
HnswSearchCandidate *HnswEntryCandidate(char *base, HnswElement em, HnswQuery * q, Relation rel, HnswSupport * support, bool loadVec);
|
||||||
void HnswUpdateMetaPage(Relation index, int updateEntry, HnswElement entryPoint, BlockNumber insertPage, ForkNumber forkNum, bool building);
|
void HnswUpdateMetaPage(Relation index, int updateEntry, HnswElement entryPoint, BlockNumber insertPage, ForkNumber forkNum, bool building);
|
||||||
void HnswSetNeighborTuple(char *base, HnswNeighborTuple ntup, HnswElement e, int m);
|
void HnswSetNeighborTuple(char *base, HnswNeighborTuple ntup, HnswElement e, int m);
|
||||||
|
|||||||
@@ -457,7 +457,7 @@ InsertTupleInMemory(HnswBuildState * buildstate, HnswElement element)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Find neighbors for element */
|
/* Find neighbors for element */
|
||||||
HnswFindElementNeighbors(base, element, entryPoint, NULL, support, m, efConstruction, false);
|
HnswFindElementNeighbors(base, element, entryPoint, NULL, support, m, efConstruction, false, true);
|
||||||
|
|
||||||
/* Update graph in memory */
|
/* Update graph in memory */
|
||||||
UpdateGraphInMemory(support, element, m, efConstruction, entryPoint, buildstate);
|
UpdateGraphInMemory(support, element, m, efConstruction, entryPoint, buildstate);
|
||||||
|
|||||||
@@ -725,7 +725,7 @@ HnswInsertTupleOnDisk(Relation index, HnswSupport * support, Datum value, ItemPo
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Find neighbors for element */
|
/* Find neighbors for element */
|
||||||
HnswFindElementNeighbors(base, element, entryPoint, index, support, m, efConstruction, false);
|
HnswFindElementNeighbors(base, element, entryPoint, index, support, m, efConstruction, false, building);
|
||||||
|
|
||||||
/* Update graph on disk */
|
/* Update graph on disk */
|
||||||
UpdateGraphOnDisk(index, support, element, m, efConstruction, entryPoint, building);
|
UpdateGraphOnDisk(index, support, element, m, efConstruction, entryPoint, building);
|
||||||
|
|||||||
@@ -37,11 +37,11 @@ GetScanItems(IndexScanDesc scan, Datum value)
|
|||||||
|
|
||||||
for (int lc = entryPoint->level; lc >= 1; lc--)
|
for (int lc = entryPoint->level; lc >= 1; lc--)
|
||||||
{
|
{
|
||||||
w = HnswSearchLayer(base, q, ep, 1, lc, index, support, m, false, NULL, NULL, NULL, true, NULL);
|
w = HnswSearchLayer(base, q, ep, 1, lc, index, support, m, false, NULL, NULL, NULL, true, NULL, false);
|
||||||
ep = w;
|
ep = w;
|
||||||
}
|
}
|
||||||
|
|
||||||
return HnswSearchLayer(base, q, ep, hnsw_ef_search, 0, index, support, m, false, NULL, &so->v, hnsw_iterative_scan != HNSW_ITERATIVE_SCAN_OFF ? &so->discarded : NULL, true, &so->tuples);
|
return HnswSearchLayer(base, q, ep, hnsw_ef_search, 0, index, support, m, false, NULL, &so->v, hnsw_iterative_scan != HNSW_ITERATIVE_SCAN_OFF ? &so->discarded : NULL, true, &so->tuples, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -72,7 +72,7 @@ ResumeScanItems(IndexScanDesc scan)
|
|||||||
ep = lappend(ep, sc);
|
ep = lappend(ep, sc);
|
||||||
}
|
}
|
||||||
|
|
||||||
return HnswSearchLayer(base, &so->q, ep, batch_size, 0, index, &so->support, so->m, false, NULL, &so->v, &so->discarded, false, &so->tuples);
|
return HnswSearchLayer(base, &so->q, ep, batch_size, 0, index, &so->support, so->m, false, NULL, &so->v, &so->discarded, false, &so->tuples, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -15,6 +15,10 @@
|
|||||||
#include "utils/memdebug.h"
|
#include "utils/memdebug.h"
|
||||||
#include "utils/rel.h"
|
#include "utils/rel.h"
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM >= 170000
|
||||||
|
#include "storage/read_stream.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#if PG_VERSION_NUM < 170000
|
#if PG_VERSION_NUM < 170000
|
||||||
static inline uint64
|
static inline uint64
|
||||||
murmurhash64(uint64 data)
|
murmurhash64(uint64 data)
|
||||||
@@ -525,14 +529,12 @@ HnswGetDistance(Datum a, Datum b, HnswSupport * support)
|
|||||||
* Load an element and optionally get its distance from q
|
* Load an element and optionally get its distance from q
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
HnswLoadElementImpl(BlockNumber blkno, OffsetNumber offno, double *distance, HnswQuery * q, Relation index, HnswSupport * support, bool loadVec, double *maxDistance, HnswElement * element)
|
HnswLoadElementImpl(Buffer buf, OffsetNumber offno, double *distance, HnswQuery * q, Relation index, HnswSupport * support, bool loadVec, double *maxDistance, HnswElement * element)
|
||||||
{
|
{
|
||||||
Buffer buf;
|
|
||||||
Page page;
|
Page page;
|
||||||
HnswElementTuple etup;
|
HnswElementTuple etup;
|
||||||
|
|
||||||
/* Read vector */
|
/* Read vector */
|
||||||
buf = ReadBuffer(index, blkno);
|
|
||||||
LockBuffer(buf, BUFFER_LOCK_SHARE);
|
LockBuffer(buf, BUFFER_LOCK_SHARE);
|
||||||
page = BufferGetPage(buf);
|
page = BufferGetPage(buf);
|
||||||
|
|
||||||
@@ -553,7 +555,7 @@ HnswLoadElementImpl(BlockNumber blkno, OffsetNumber offno, double *distance, Hns
|
|||||||
if (distance == NULL || maxDistance == NULL || *distance < *maxDistance)
|
if (distance == NULL || maxDistance == NULL || *distance < *maxDistance)
|
||||||
{
|
{
|
||||||
if (*element == NULL)
|
if (*element == NULL)
|
||||||
*element = HnswInitElementFromBlock(blkno, offno);
|
*element = HnswInitElementFromBlock(BufferGetBlockNumber(buf), offno);
|
||||||
|
|
||||||
HnswLoadElementFromTuple(*element, etup, true, loadVec);
|
HnswLoadElementFromTuple(*element, etup, true, loadVec);
|
||||||
}
|
}
|
||||||
@@ -567,7 +569,9 @@ HnswLoadElementImpl(BlockNumber blkno, OffsetNumber offno, double *distance, Hns
|
|||||||
void
|
void
|
||||||
HnswLoadElement(HnswElement element, double *distance, HnswQuery * q, Relation index, HnswSupport * support, bool loadVec, double *maxDistance)
|
HnswLoadElement(HnswElement element, double *distance, HnswQuery * q, Relation index, HnswSupport * support, bool loadVec, double *maxDistance)
|
||||||
{
|
{
|
||||||
HnswLoadElementImpl(element->blkno, element->offno, distance, q, index, support, loadVec, maxDistance, &element);
|
Buffer buf = ReadBuffer(index, element->blkno);
|
||||||
|
|
||||||
|
HnswLoadElementImpl(buf, element->offno, distance, q, index, support, loadVec, maxDistance, &element);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -807,11 +811,31 @@ HnswLoadUnvisitedFromDisk(HnswElement element, HnswUnvisited * unvisited, int *u
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM >= 170000
|
||||||
|
/*
|
||||||
|
* Get next block number for read stream
|
||||||
|
*/
|
||||||
|
static BlockNumber
|
||||||
|
HnswReadStreamNextBlock(ReadStream *stream, void *callback_private_data, void *per_buffer_data)
|
||||||
|
{
|
||||||
|
HnswReadStreamData *streamData = callback_private_data;
|
||||||
|
OffsetNumber *offno = per_buffer_data;
|
||||||
|
HnswUnvisited *uv;
|
||||||
|
|
||||||
|
if (streamData->visited == streamData->unvisitedLength)
|
||||||
|
return InvalidBlockNumber;
|
||||||
|
|
||||||
|
uv = &streamData->unvisited[streamData->visited++];
|
||||||
|
*offno = ItemPointerGetOffsetNumber(&uv->indextid);
|
||||||
|
return ItemPointerGetBlockNumber(&uv->indextid);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Algorithm 2 from paper
|
* Algorithm 2 from paper
|
||||||
*/
|
*/
|
||||||
List *
|
List *
|
||||||
HnswSearchLayer(char *base, HnswQuery * q, List *ep, int ef, int lc, Relation index, HnswSupport * support, int m, bool inserting, HnswElement skipElement, visited_hash * v, pairingheap **discarded, bool initVisited, int64 *tuples)
|
HnswSearchLayer(char *base, HnswQuery * q, List *ep, int ef, int lc, Relation index, HnswSupport * support, int m, bool inserting, HnswElement skipElement, visited_hash * v, pairingheap **discarded, bool initVisited, int64 *tuples, bool maintenance)
|
||||||
{
|
{
|
||||||
List *w = NIL;
|
List *w = NIL;
|
||||||
pairingheap *C = pairingheap_allocate(CompareNearestCandidates, NULL);
|
pairingheap *C = pairingheap_allocate(CompareNearestCandidates, NULL);
|
||||||
@@ -826,6 +850,18 @@ HnswSearchLayer(char *base, HnswQuery * q, List *ep, int ef, int lc, Relation in
|
|||||||
int unvisitedLength;
|
int unvisitedLength;
|
||||||
bool inMemory = index == NULL;
|
bool inMemory = index == NULL;
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM >= 170000
|
||||||
|
HnswReadStreamData streamData;
|
||||||
|
ReadStream *stream = NULL;
|
||||||
|
|
||||||
|
if (!inMemory)
|
||||||
|
{
|
||||||
|
int flags = maintenance ? READ_STREAM_MAINTENANCE : READ_STREAM_DEFAULT;
|
||||||
|
|
||||||
|
stream = read_stream_begin_relation(flags, NULL, index, MAIN_FORKNUM, HnswReadStreamNextBlock, &streamData, sizeof(OffsetNumber));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (v == NULL)
|
if (v == NULL)
|
||||||
{
|
{
|
||||||
v = &vh;
|
v = &vh;
|
||||||
@@ -888,8 +924,18 @@ HnswSearchLayer(char *base, HnswQuery * q, List *ep, int ef, int lc, Relation in
|
|||||||
if (inMemory)
|
if (inMemory)
|
||||||
HnswLoadUnvisitedFromMemory(base, cElement, unvisited, &unvisitedLength, v, lc, localNeighborhood, neighborhoodSize);
|
HnswLoadUnvisitedFromMemory(base, cElement, unvisited, &unvisitedLength, v, lc, localNeighborhood, neighborhoodSize);
|
||||||
else
|
else
|
||||||
|
{
|
||||||
HnswLoadUnvisitedFromDisk(cElement, unvisited, &unvisitedLength, v, index, m, lm, lc);
|
HnswLoadUnvisitedFromDisk(cElement, unvisited, &unvisitedLength, v, index, m, lm, lc);
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM >= 170000
|
||||||
|
read_stream_reset(stream);
|
||||||
|
|
||||||
|
streamData.unvisited = unvisited;
|
||||||
|
streamData.unvisitedLength = unvisitedLength;
|
||||||
|
streamData.visited = 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/* OK to count elements instead of tuples */
|
/* OK to count elements instead of tuples */
|
||||||
if (tuples != NULL)
|
if (tuples != NULL)
|
||||||
(*tuples) += unvisitedLength;
|
(*tuples) += unvisitedLength;
|
||||||
@@ -910,13 +956,24 @@ HnswSearchLayer(char *base, HnswQuery * q, List *ep, int ef, int lc, Relation in
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Buffer buf;
|
||||||
|
OffsetNumber offno;
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM >= 170000
|
||||||
|
void *offnoPtr;
|
||||||
|
|
||||||
|
buf = read_stream_next_buffer(stream, &offnoPtr);
|
||||||
|
offno = *((OffsetNumber *) offnoPtr);
|
||||||
|
#else
|
||||||
ItemPointer indextid = &unvisited[i].indextid;
|
ItemPointer indextid = &unvisited[i].indextid;
|
||||||
BlockNumber blkno = ItemPointerGetBlockNumber(indextid);
|
|
||||||
OffsetNumber offno = ItemPointerGetOffsetNumber(indextid);
|
buf = ReadBuffer(index, ItemPointerGetBlockNumber(indextid));
|
||||||
|
offno = ItemPointerGetOffsetNumber(indextid);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Avoid any allocations if not adding */
|
/* Avoid any allocations if not adding */
|
||||||
eElement = NULL;
|
eElement = NULL;
|
||||||
HnswLoadElementImpl(blkno, offno, &eDistance, q, index, support, inserting, alwaysAdd || discarded != NULL ? NULL : &f->distance, &eElement);
|
HnswLoadElementImpl(buf, offno, &eDistance, q, index, support, inserting, alwaysAdd || discarded != NULL ? NULL : &f->distance, &eElement);
|
||||||
|
|
||||||
if (eElement == NULL)
|
if (eElement == NULL)
|
||||||
continue;
|
continue;
|
||||||
@@ -972,6 +1029,11 @@ HnswSearchLayer(char *base, HnswQuery * q, List *ep, int ef, int lc, Relation in
|
|||||||
w = lappend(w, sc);
|
w = lappend(w, sc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM >= 170000
|
||||||
|
if (!inMemory)
|
||||||
|
read_stream_end(stream);
|
||||||
|
#endif
|
||||||
|
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1267,7 +1329,7 @@ PrecomputeHash(char *base, HnswElement element)
|
|||||||
* Algorithm 1 from paper
|
* Algorithm 1 from paper
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
HnswFindElementNeighbors(char *base, HnswElement element, HnswElement entryPoint, Relation index, HnswSupport * support, int m, int efConstruction, bool existing)
|
HnswFindElementNeighbors(char *base, HnswElement element, HnswElement entryPoint, Relation index, HnswSupport * support, int m, int efConstruction, bool existing, bool maintenance)
|
||||||
{
|
{
|
||||||
List *ep;
|
List *ep;
|
||||||
List *w;
|
List *w;
|
||||||
@@ -1294,7 +1356,7 @@ HnswFindElementNeighbors(char *base, HnswElement element, HnswElement entryPoint
|
|||||||
/* 1st phase: greedy search to insert level */
|
/* 1st phase: greedy search to insert level */
|
||||||
for (int lc = entryLevel; lc >= level + 1; lc--)
|
for (int lc = entryLevel; lc >= level + 1; lc--)
|
||||||
{
|
{
|
||||||
w = HnswSearchLayer(base, &q, ep, 1, lc, index, support, m, true, skipElement, NULL, NULL, true, NULL);
|
w = HnswSearchLayer(base, &q, ep, 1, lc, index, support, m, true, skipElement, NULL, NULL, true, NULL, maintenance);
|
||||||
ep = w;
|
ep = w;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1313,7 +1375,7 @@ HnswFindElementNeighbors(char *base, HnswElement element, HnswElement entryPoint
|
|||||||
List *lw = NIL;
|
List *lw = NIL;
|
||||||
ListCell *lc2;
|
ListCell *lc2;
|
||||||
|
|
||||||
w = HnswSearchLayer(base, &q, ep, efConstruction, lc, index, support, m, true, skipElement, NULL, NULL, true, NULL);
|
w = HnswSearchLayer(base, &q, ep, efConstruction, lc, index, support, m, true, skipElement, NULL, NULL, true, NULL, maintenance);
|
||||||
|
|
||||||
/* Convert search candidates to candidates */
|
/* Convert search candidates to candidates */
|
||||||
foreach(lc2, w)
|
foreach(lc2, w)
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ RepairGraphElement(HnswVacuumState * vacuumstate, HnswElement element, HnswEleme
|
|||||||
element->heaptidsLength = 0;
|
element->heaptidsLength = 0;
|
||||||
|
|
||||||
/* Find neighbors for element, skipping itself */
|
/* Find neighbors for element, skipping itself */
|
||||||
HnswFindElementNeighbors(base, element, entryPoint, index, support, m, efConstruction, true);
|
HnswFindElementNeighbors(base, element, entryPoint, index, support, m, efConstruction, true, true);
|
||||||
|
|
||||||
/* Zero memory for each element */
|
/* Zero memory for each element */
|
||||||
MemSet(ntup, 0, HNSW_TUPLE_ALLOC_SIZE);
|
MemSet(ntup, 0, HNSW_TUPLE_ALLOC_SIZE);
|
||||||
|
|||||||
@@ -1023,6 +1023,10 @@ ivfflatbuild(Relation heap, Relation index, IndexInfo *indexInfo)
|
|||||||
IndexBuildResult *result;
|
IndexBuildResult *result;
|
||||||
IvfflatBuildState buildstate;
|
IvfflatBuildState buildstate;
|
||||||
|
|
||||||
|
#ifdef IVFFLAT_BENCH
|
||||||
|
SeedRandom(42);
|
||||||
|
#endif
|
||||||
|
|
||||||
BuildIndex(heap, index, indexInfo, &buildstate, MAIN_FORKNUM);
|
BuildIndex(heap, index, indexInfo, &buildstate, MAIN_FORKNUM);
|
||||||
|
|
||||||
result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
|
result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
|
||||||
|
|||||||
@@ -186,6 +186,11 @@ ivfflathandler(PG_FUNCTION_ARGS)
|
|||||||
amroutine->amoptsprocnum = 0;
|
amroutine->amoptsprocnum = 0;
|
||||||
amroutine->amcanorder = false;
|
amroutine->amcanorder = false;
|
||||||
amroutine->amcanorderbyop = true;
|
amroutine->amcanorderbyop = true;
|
||||||
|
#if PG_VERSION_NUM >= 180000
|
||||||
|
amroutine->amcanhash = false;
|
||||||
|
amroutine->amconsistentequality = false;
|
||||||
|
amroutine->amconsistentordering = false;
|
||||||
|
#endif
|
||||||
amroutine->amcanbackward = false; /* can change direction mid-scan */
|
amroutine->amcanbackward = false; /* can change direction mid-scan */
|
||||||
amroutine->amcanunique = false;
|
amroutine->amcanunique = false;
|
||||||
amroutine->amcanmulticol = false;
|
amroutine->amcanmulticol = false;
|
||||||
@@ -218,6 +223,9 @@ ivfflathandler(PG_FUNCTION_ARGS)
|
|||||||
amroutine->amvacuumcleanup = ivfflatvacuumcleanup;
|
amroutine->amvacuumcleanup = ivfflatvacuumcleanup;
|
||||||
amroutine->amcanreturn = NULL; /* tuple not included in heapsort */
|
amroutine->amcanreturn = NULL; /* tuple not included in heapsort */
|
||||||
amroutine->amcostestimate = ivfflatcostestimate;
|
amroutine->amcostestimate = ivfflatcostestimate;
|
||||||
|
#if PG_VERSION_NUM >= 180000
|
||||||
|
amroutine->amgettreeheight = NULL;
|
||||||
|
#endif
|
||||||
amroutine->amoptions = ivfflatoptions;
|
amroutine->amoptions = ivfflatoptions;
|
||||||
amroutine->amproperty = NULL; /* TODO AMPROP_DISTANCE_ORDERABLE */
|
amroutine->amproperty = NULL; /* TODO AMPROP_DISTANCE_ORDERABLE */
|
||||||
amroutine->ambuildphasename = ivfflatbuildphasename;
|
amroutine->ambuildphasename = ivfflatbuildphasename;
|
||||||
@@ -238,5 +246,10 @@ ivfflathandler(PG_FUNCTION_ARGS)
|
|||||||
amroutine->aminitparallelscan = NULL;
|
amroutine->aminitparallelscan = NULL;
|
||||||
amroutine->amparallelrescan = NULL;
|
amroutine->amparallelrescan = NULL;
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM >= 180000
|
||||||
|
amroutine->amtranslatestrategy = NULL;
|
||||||
|
amroutine->amtranslatecmptype = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
PG_RETURN_POINTER(amroutine);
|
PG_RETURN_POINTER(amroutine);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,9 +73,11 @@
|
|||||||
#if PG_VERSION_NUM >= 150000
|
#if PG_VERSION_NUM >= 150000
|
||||||
#define RandomDouble() pg_prng_double(&pg_global_prng_state)
|
#define RandomDouble() pg_prng_double(&pg_global_prng_state)
|
||||||
#define RandomInt() pg_prng_uint32(&pg_global_prng_state)
|
#define RandomInt() pg_prng_uint32(&pg_global_prng_state)
|
||||||
|
#define SeedRandom(seed) pg_prng_seed(&pg_global_prng_state, seed)
|
||||||
#else
|
#else
|
||||||
#define RandomDouble() (((double) random()) / MAX_RANDOM_VALUE)
|
#define RandomDouble() (((double) random()) / MAX_RANDOM_VALUE)
|
||||||
#define RandomInt() random()
|
#define RandomInt() random()
|
||||||
|
#define SeedRandom(seed) srandom(seed)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Variables */
|
/* Variables */
|
||||||
|
|||||||
@@ -259,8 +259,8 @@ VectorUpdateCenter(Pointer v, int dimensions, float *x)
|
|||||||
SET_VARSIZE(vec, VECTOR_SIZE(dimensions));
|
SET_VARSIZE(vec, VECTOR_SIZE(dimensions));
|
||||||
vec->dim = dimensions;
|
vec->dim = dimensions;
|
||||||
|
|
||||||
for (int k = 0; k < dimensions; k++)
|
for (int i = 0; i < dimensions; i++)
|
||||||
vec->x[k] = x[k];
|
vec->x[i] = x[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -271,8 +271,8 @@ HalfvecUpdateCenter(Pointer v, int dimensions, float *x)
|
|||||||
SET_VARSIZE(vec, HALFVEC_SIZE(dimensions));
|
SET_VARSIZE(vec, HALFVEC_SIZE(dimensions));
|
||||||
vec->dim = dimensions;
|
vec->dim = dimensions;
|
||||||
|
|
||||||
for (int k = 0; k < dimensions; k++)
|
for (int i = 0; i < dimensions; i++)
|
||||||
vec->x[k] = Float4ToHalfUnchecked(x[k]);
|
vec->x[i] = Float4ToHalfUnchecked(x[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -284,29 +284,33 @@ BitUpdateCenter(Pointer v, int dimensions, float *x)
|
|||||||
SET_VARSIZE(vec, VARBITTOTALLEN(dimensions));
|
SET_VARSIZE(vec, VARBITTOTALLEN(dimensions));
|
||||||
VARBITLEN(vec) = dimensions;
|
VARBITLEN(vec) = dimensions;
|
||||||
|
|
||||||
for (uint32 k = 0; k < VARBITBYTES(vec); k++)
|
for (uint32 i = 0; i < VARBITBYTES(vec); i++)
|
||||||
nx[k] = 0;
|
nx[i] = 0;
|
||||||
|
|
||||||
for (int k = 0; k < dimensions; k++)
|
for (int i = 0; i < dimensions; i++)
|
||||||
nx[k / 8] |= (x[k] > 0.5 ? 1 : 0) << (7 - (k % 8));
|
nx[i / 8] |= (x[i] > 0.5 ? 1 : 0) << (7 - (i % 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
VectorSumCenter(Pointer v, float *x)
|
VectorSumCenter(Pointer v, float *x)
|
||||||
{
|
{
|
||||||
Vector *vec = (Vector *) v;
|
Vector *vec = (Vector *) v;
|
||||||
|
int dim = vec->dim;
|
||||||
|
|
||||||
for (int k = 0; k < vec->dim; k++)
|
/* Auto-vectorized */
|
||||||
x[k] += vec->x[k];
|
for (int i = 0; i < dim; i++)
|
||||||
|
x[i] += vec->x[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
HalfvecSumCenter(Pointer v, float *x)
|
HalfvecSumCenter(Pointer v, float *x)
|
||||||
{
|
{
|
||||||
HalfVector *vec = (HalfVector *) v;
|
HalfVector *vec = (HalfVector *) v;
|
||||||
|
int dim = vec->dim;
|
||||||
|
|
||||||
for (int k = 0; k < vec->dim; k++)
|
/* Auto-vectorized on aarch64 */
|
||||||
x[k] += HalfToFloat4(vec->x[k]);
|
for (int i = 0; i < dim; i++)
|
||||||
|
x[i] += HalfToFloat4(vec->x[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -314,8 +318,8 @@ BitSumCenter(Pointer v, float *x)
|
|||||||
{
|
{
|
||||||
VarBit *vec = (VarBit *) v;
|
VarBit *vec = (VarBit *) v;
|
||||||
|
|
||||||
for (int k = 0; k < VARBITLEN(vec); k++)
|
for (int i = 0; i < VARBITLEN(vec); i++)
|
||||||
x[k] += (float) (((VARBITS(vec)[k / 8]) >> (7 - (k % 8))) & 0x01);
|
x[i] += (float) (((VARBITS(vec)[i / 8]) >> (7 - (i % 8))) & 0x01);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
10
src/vector.c
10
src/vector.c
@@ -916,15 +916,19 @@ vector_concat(PG_FUNCTION_ARGS)
|
|||||||
Vector *b = PG_GETARG_VECTOR_P(1);
|
Vector *b = PG_GETARG_VECTOR_P(1);
|
||||||
Vector *result;
|
Vector *result;
|
||||||
int dim = a->dim + b->dim;
|
int dim = a->dim + b->dim;
|
||||||
|
int dim_a = a->dim;
|
||||||
|
int dim_b = b->dim;
|
||||||
|
|
||||||
CheckDim(dim);
|
CheckDim(dim);
|
||||||
result = InitVector(dim);
|
result = InitVector(dim);
|
||||||
|
|
||||||
for (int i = 0; i < a->dim; i++)
|
/* Auto-vectorized */
|
||||||
|
for (int i = 0; i < dim_a; i++)
|
||||||
result->x[i] = a->x[i];
|
result->x[i] = a->x[i];
|
||||||
|
|
||||||
for (int i = 0; i < b->dim; i++)
|
/* Auto-vectorized */
|
||||||
result->x[i + a->dim] = b->x[i];
|
for (int i = 0; i < dim_b; i++)
|
||||||
|
result->x[i + dim_a] = b->x[i];
|
||||||
|
|
||||||
PG_RETURN_POINTER(result);
|
PG_RETURN_POINTER(result);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user