diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bd8f465..517ca8b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,3 +42,16 @@ jobs: wget -q https://github.com/postgres/postgres/archive/refs/tags/REL_14_5.tar.gz tar xf REL_14_5.tar.gz make prove_installcheck PROVE=prove PROVE_FLAGS="-I ./postgres-REL_14_5/src/test/perl" PERL5LIB="/Users/runner/perl5/lib/perl5" + windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v3 + - uses: ankane/setup-postgres@v1 + with: + postgres-version: 14 + - run: | + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + nmake /NOLOGO /F Makefile.win + nmake /NOLOGO /F Makefile.win install + nmake /NOLOGO /F Makefile.win installcheck + shell: cmd diff --git a/CHANGELOG.md b/CHANGELOG.md index f7fb786..e444b74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ - Changed text representation for vector elements to match `real` - Improved accuracy of text parsing for certain inputs +- Added experimental support for Windows ## 0.3.2 (2022-11-22) diff --git a/Makefile.win b/Makefile.win new file mode 100644 index 0000000..eb4b1d4 --- /dev/null +++ b/Makefile.win @@ -0,0 +1,65 @@ +EXTENSION = vector +EXTVERSION = 0.3.2 + +OBJS = src\ivfbuild.obj src\ivfflat.obj src\ivfinsert.obj src\ivfkmeans.obj src\ivfscan.obj src\ivfutils.obj src\ivfvacuum.obj src\vector.obj + +# For /arch flags +# https://learn.microsoft.com/en-us/cpp/build/reference/arch-minimum-cpu-architecture +OPTFLAGS = + +# For auto-vectorization: +# - MSVC (needs /O2 /fp:fast) - https://learn.microsoft.com/en-us/cpp/parallel/auto-parallelization-and-auto-vectorization?#auto-vectorizer +PG_CFLAGS = $(PG_CFLAGS) $(OPTFLAGS) /O2 /fp:fast + +# Debug MSVC auto-vectorization +# https://learn.microsoft.com/en-us/cpp/error-messages/tool-errors/vectorizer-and-parallelizer-messages +# PG_CFLAGS = $(PG_CFLAGS) /Qvec-report:2 + +all: sql\$(EXTENSION)--$(EXTVERSION).sql + +sql\$(EXTENSION)--$(EXTVERSION).sql: sql\$(EXTENSION).sql + copy sql\$(EXTENSION).sql $@ + +# TODO use pg_config +INCLUDEDIR = $(PGROOT)\include +INCLUDEDIR_SERVER = $(PGROOT)\include\server +LIBDIR = $(PGROOT)\lib +PKGLIBDIR = $(PGROOT)\lib +SHAREDIR = $(PGROOT)\share + +CFLAGS = /nologo /I"$(INCLUDEDIR_SERVER)\port\win32_msvc" /I"$(INCLUDEDIR_SERVER)\port\win32" /I"$(INCLUDEDIR_SERVER)" /I"$(INCLUDEDIR)" + +CFLAGS = $(CFLAGS) $(PG_CFLAGS) + +SHLIB = src\$(EXTENSION).dll + +LIBS = "$(LIBDIR)\postgres.lib" + +.c.obj: + $(CC) $(CFLAGS) /c $< /Fo$@ + +$(SHLIB): $(OBJS) + $(CC) $(CFLAGS) $(OBJS) $(LIBS) /link /DLL /OUT:$(SHLIB) + +all: $(SHLIB) + +install: + copy $(SHLIB) "$(PKGLIBDIR)" + copy $(EXTENSION).control "$(SHAREDIR)\extension" + copy sql\$(EXTENSION)--*.sql "$(SHAREDIR)\extension" + +# TODO improve +installcheck: + createdb contrib_regression + mkdir -p results + psql -d contrib_regression -a -q -f test\sql\btree.sql > results\btree.out 2>&1 + psql -d contrib_regression -a -q -f test\sql\cast.sql > results\cast.out 2>&1 + psql -d contrib_regression -a -q -f test\sql\copy.sql > results\copy.out 2>&1 + psql -d contrib_regression -a -q -f test\sql\functions.sql > results\functions.out 2>&1 + psql -d contrib_regression -a -q -f test\sql\input.sql > results\input.out 2>&1 + psql -d contrib_regression -a -q -f test\sql\ivfflat_cosine.sql > results\ivfflat_cosine.out 2>&1 + psql -d contrib_regression -a -q -f test\sql\ivfflat_ip.sql > results\ivfflat_ip.out 2>&1 + psql -d contrib_regression -a -q -f test\sql\ivfflat_l2.sql > results\ivfflat_l2.out 2>&1 + psql -d contrib_regression -a -q -f test\sql\ivfflat_options.sql > results\ivfflat_options.out 2>&1 + psql -d contrib_regression -a -q -f test\sql\ivfflat_unlogged.sql > results\ivfflat_unlogged.out 2>&1 + cat results/* diff --git a/src/ivfflat.c b/src/ivfflat.c index 35c0e48..0829458 100644 --- a/src/ivfflat.c +++ b/src/ivfflat.c @@ -164,7 +164,7 @@ ivfflatvalidate(Oid opclassoid) * * See https://www.postgresql.org/docs/current/index-api.html */ -PG_FUNCTION_INFO_V1(ivfflathandler); +PGDLLEXPORT PG_FUNCTION_INFO_V1(ivfflathandler); Datum ivfflathandler(PG_FUNCTION_ARGS) { diff --git a/src/ivfflat.h b/src/ivfflat.h index 13c3425..0aafe20 100644 --- a/src/ivfflat.h +++ b/src/ivfflat.h @@ -78,6 +78,9 @@ /* Variables */ extern int ivfflat_probes; +/* Exported functions */ +PGDLLEXPORT void _PG_init(void); + typedef struct VectorArrayData { int length; @@ -210,7 +213,6 @@ typedef IvfflatScanOpaqueData * IvfflatScanOpaque; #define VectorArraySet(_arr, _offset, _val) (memcpy(VECTOR_ARRAY_OFFSET(_arr, _offset), _val, VECTOR_SIZE(_arr->dim))) /* Methods */ -void _PG_init(void); VectorArray VectorArrayInit(int maxlen, int dimensions); void VectorArrayFree(VectorArray arr); void PrintVectorArray(char *msg, VectorArray arr); diff --git a/src/vector.c b/src/vector.c index 2661282..22a9853 100644 --- a/src/vector.c +++ b/src/vector.c @@ -109,7 +109,7 @@ PrintVector(char *msg, Vector * vector) /* * Convert textual representation to internal representation */ -PG_FUNCTION_INFO_V1(vector_in); +PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_in); Datum vector_in(PG_FUNCTION_ARGS) { @@ -186,7 +186,7 @@ vector_in(PG_FUNCTION_ARGS) /* * Convert internal representation to textual representation */ -PG_FUNCTION_INFO_V1(vector_out); +PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_out); Datum vector_out(PG_FUNCTION_ARGS) { @@ -247,7 +247,7 @@ vector_out(PG_FUNCTION_ARGS) /* * Convert type modifier */ -PG_FUNCTION_INFO_V1(vector_typmod_in); +PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_typmod_in); Datum vector_typmod_in(PG_FUNCTION_ARGS) { @@ -278,7 +278,7 @@ vector_typmod_in(PG_FUNCTION_ARGS) /* * Convert external binary representation to internal representation */ -PG_FUNCTION_INFO_V1(vector_recv); +PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_recv); Datum vector_recv(PG_FUNCTION_ARGS) { @@ -310,7 +310,7 @@ vector_recv(PG_FUNCTION_ARGS) /* * Convert internal representation to the external binary representation */ -PG_FUNCTION_INFO_V1(vector_send); +PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_send); Datum vector_send(PG_FUNCTION_ARGS) { @@ -330,7 +330,7 @@ vector_send(PG_FUNCTION_ARGS) /* * Convert vector to vector */ -PG_FUNCTION_INFO_V1(vector); +PGDLLEXPORT PG_FUNCTION_INFO_V1(vector); Datum vector(PG_FUNCTION_ARGS) { @@ -345,7 +345,7 @@ vector(PG_FUNCTION_ARGS) /* * Convert array to vector */ -PG_FUNCTION_INFO_V1(array_to_vector); +PGDLLEXPORT PG_FUNCTION_INFO_V1(array_to_vector); Datum array_to_vector(PG_FUNCTION_ARGS) { @@ -403,7 +403,7 @@ array_to_vector(PG_FUNCTION_ARGS) /* * Convert vector to float4[] */ -PG_FUNCTION_INFO_V1(vector_to_float4); +PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_to_float4); Datum vector_to_float4(PG_FUNCTION_ARGS) { @@ -426,12 +426,14 @@ vector_to_float4(PG_FUNCTION_ARGS) /* * Get the L2 distance between vectors */ -PG_FUNCTION_INFO_V1(l2_distance); +PGDLLEXPORT PG_FUNCTION_INFO_V1(l2_distance); Datum l2_distance(PG_FUNCTION_ARGS) { Vector *a = PG_GETARG_VECTOR_P(0); Vector *b = PG_GETARG_VECTOR_P(1); + float *ax = a->x; + float *bx = b->x; double distance = 0.0; double diff; @@ -439,7 +441,7 @@ l2_distance(PG_FUNCTION_ARGS) for (int i = 0; i < a->dim; i++) { - diff = a->x[i] - b->x[i]; + diff = ax[i] - bx[i]; distance += diff * diff; } @@ -450,12 +452,14 @@ l2_distance(PG_FUNCTION_ARGS) * Get the L2 squared distance between vectors * This saves a sqrt calculation */ -PG_FUNCTION_INFO_V1(vector_l2_squared_distance); +PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_l2_squared_distance); Datum vector_l2_squared_distance(PG_FUNCTION_ARGS) { Vector *a = PG_GETARG_VECTOR_P(0); Vector *b = PG_GETARG_VECTOR_P(1); + float *ax = a->x; + float *bx = b->x; double distance = 0.0; double diff; @@ -463,7 +467,7 @@ vector_l2_squared_distance(PG_FUNCTION_ARGS) for (int i = 0; i < a->dim; i++) { - diff = a->x[i] - b->x[i]; + diff = ax[i] - bx[i]; distance += diff * diff; } @@ -473,18 +477,20 @@ vector_l2_squared_distance(PG_FUNCTION_ARGS) /* * Get the inner product of two vectors */ -PG_FUNCTION_INFO_V1(inner_product); +PGDLLEXPORT PG_FUNCTION_INFO_V1(inner_product); Datum inner_product(PG_FUNCTION_ARGS) { Vector *a = PG_GETARG_VECTOR_P(0); Vector *b = PG_GETARG_VECTOR_P(1); + float *ax = a->x; + float *bx = b->x; double distance = 0.0; CheckDims(a, b); for (int i = 0; i < a->dim; i++) - distance += a->x[i] * b->x[i]; + distance += ax[i] * bx[i]; PG_RETURN_FLOAT8(distance); } @@ -492,18 +498,20 @@ inner_product(PG_FUNCTION_ARGS) /* * Get the negative inner product of two vectors */ -PG_FUNCTION_INFO_V1(vector_negative_inner_product); +PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_negative_inner_product); Datum vector_negative_inner_product(PG_FUNCTION_ARGS) { Vector *a = PG_GETARG_VECTOR_P(0); Vector *b = PG_GETARG_VECTOR_P(1); + float *ax = a->x; + float *bx = b->x; double distance = 0.0; CheckDims(a, b); for (int i = 0; i < a->dim; i++) - distance += a->x[i] * b->x[i]; + distance += ax[i] * bx[i]; PG_RETURN_FLOAT8(distance * -1); } @@ -511,12 +519,14 @@ vector_negative_inner_product(PG_FUNCTION_ARGS) /* * Get the cosine distance between two vectors */ -PG_FUNCTION_INFO_V1(cosine_distance); +PGDLLEXPORT PG_FUNCTION_INFO_V1(cosine_distance); Datum cosine_distance(PG_FUNCTION_ARGS) { Vector *a = PG_GETARG_VECTOR_P(0); Vector *b = PG_GETARG_VECTOR_P(1); + float *ax = a->x; + float *bx = b->x; double distance = 0.0; double norma = 0.0; double normb = 0.0; @@ -525,9 +535,9 @@ cosine_distance(PG_FUNCTION_ARGS) for (int i = 0; i < a->dim; i++) { - distance += a->x[i] * b->x[i]; - norma += a->x[i] * a->x[i]; - normb += b->x[i] * b->x[i]; + distance += ax[i] * bx[i]; + norma += ax[i] * ax[i]; + normb += bx[i] * bx[i]; } PG_RETURN_FLOAT8(1 - (distance / (sqrt(norma) * sqrt(normb)))); @@ -538,7 +548,7 @@ cosine_distance(PG_FUNCTION_ARGS) * Currently uses angular distance since needs to satisfy triangle inequality * Assumes inputs are unit vectors (skips norm) */ -PG_FUNCTION_INFO_V1(vector_spherical_distance); +PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_spherical_distance); Datum vector_spherical_distance(PG_FUNCTION_ARGS) { @@ -563,7 +573,7 @@ vector_spherical_distance(PG_FUNCTION_ARGS) /* * Get the dimensions of a vector */ -PG_FUNCTION_INFO_V1(vector_dims); +PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_dims); Datum vector_dims(PG_FUNCTION_ARGS) { @@ -575,15 +585,16 @@ vector_dims(PG_FUNCTION_ARGS) /* * Get the L2 norm of a vector */ -PG_FUNCTION_INFO_V1(vector_norm); +PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_norm); Datum vector_norm(PG_FUNCTION_ARGS) { Vector *a = PG_GETARG_VECTOR_P(0); + float *ax = a->x; double norm = 0.0; for (int i = 0; i < a->dim; i++) - norm += a->x[i] * a->x[i]; + norm += ax[i] * ax[i]; PG_RETURN_FLOAT8(sqrt(norm)); } @@ -591,20 +602,23 @@ vector_norm(PG_FUNCTION_ARGS) /* * Add vectors */ -PG_FUNCTION_INFO_V1(vector_add); +PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_add); Datum vector_add(PG_FUNCTION_ARGS) { Vector *a = PG_GETARG_VECTOR_P(0); Vector *b = PG_GETARG_VECTOR_P(1); + float *ax = a->x; + float *bx = b->x; Vector *result; - int i; + float *rx; CheckDims(a, b); result = InitVector(a->dim); - for (i = 0; i < a->dim; i++) - result->x[i] = a->x[i] + b->x[i]; + rx = result->x; + for (int i = 0, imax = a->dim; i < imax; i++) + rx[i] = ax[i] + bx[i]; PG_RETURN_POINTER(result); } @@ -612,20 +626,23 @@ vector_add(PG_FUNCTION_ARGS) /* * Subtract vectors */ -PG_FUNCTION_INFO_V1(vector_sub); +PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_sub); Datum vector_sub(PG_FUNCTION_ARGS) { Vector *a = PG_GETARG_VECTOR_P(0); Vector *b = PG_GETARG_VECTOR_P(1); + float *ax = a->x; + float *bx = b->x; Vector *result; - int i; + float *rx; CheckDims(a, b); result = InitVector(a->dim); - for (i = 0; i < a->dim; i++) - result->x[i] = a->x[i] - b->x[i]; + rx = result->x; + for (int i = 0, imax = a->dim; i < imax; i++) + rx[i] = ax[i] - bx[i]; PG_RETURN_POINTER(result); } @@ -654,7 +671,7 @@ vector_cmp_internal(Vector * a, Vector * b) /* * Less than */ -PG_FUNCTION_INFO_V1(vector_lt); +PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_lt); Datum vector_lt(PG_FUNCTION_ARGS) { @@ -667,7 +684,7 @@ vector_lt(PG_FUNCTION_ARGS) /* * Less than or equal */ -PG_FUNCTION_INFO_V1(vector_le); +PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_le); Datum vector_le(PG_FUNCTION_ARGS) { @@ -680,7 +697,7 @@ vector_le(PG_FUNCTION_ARGS) /* * Equal */ -PG_FUNCTION_INFO_V1(vector_eq); +PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_eq); Datum vector_eq(PG_FUNCTION_ARGS) { @@ -693,7 +710,7 @@ vector_eq(PG_FUNCTION_ARGS) /* * Not equal */ -PG_FUNCTION_INFO_V1(vector_ne); +PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_ne); Datum vector_ne(PG_FUNCTION_ARGS) { @@ -706,7 +723,7 @@ vector_ne(PG_FUNCTION_ARGS) /* * Greater than or equal */ -PG_FUNCTION_INFO_V1(vector_ge); +PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_ge); Datum vector_ge(PG_FUNCTION_ARGS) { @@ -719,7 +736,7 @@ vector_ge(PG_FUNCTION_ARGS) /* * Greater than */ -PG_FUNCTION_INFO_V1(vector_gt); +PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_gt); Datum vector_gt(PG_FUNCTION_ARGS) { @@ -732,7 +749,7 @@ vector_gt(PG_FUNCTION_ARGS) /* * Compare vectors */ -PG_FUNCTION_INFO_V1(vector_cmp); +PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_cmp); Datum vector_cmp(PG_FUNCTION_ARGS) {