diff --git a/Makefile b/Makefile index d665f2e..bab529b 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ EXTVERSION = 0.6.2 MODULE_big = vector DATA = $(wildcard sql/*--*.sql) -OBJS = src/bitvector.o src/halfutils.o src/halfvec.o src/hnsw.o src/hnswbuild.o src/hnswinsert.o src/hnswscan.o src/hnswutils.o src/hnswvacuum.o src/ivfbuild.o src/ivfflat.o src/ivfinsert.o src/ivfkmeans.o src/ivfscan.o src/ivfutils.o src/ivfvacuum.o src/sparsevec.o src/vector.o +OBJS = src/bitutils.o src/bitvector.o src/halfutils.o src/halfvec.o src/hnsw.o src/hnswbuild.o src/hnswinsert.o src/hnswscan.o src/hnswutils.o src/hnswvacuum.o src/ivfbuild.o src/ivfflat.o src/ivfinsert.o src/ivfkmeans.o src/ivfscan.o src/ivfutils.o src/ivfvacuum.o src/sparsevec.o src/vector.o HEADERS = src/halfvec.h src/sparsevec.h src/vector.h TESTS = $(wildcard test/sql/*.sql) diff --git a/Makefile.win b/Makefile.win index 32a3786..3815c62 100644 --- a/Makefile.win +++ b/Makefile.win @@ -1,7 +1,7 @@ EXTENSION = vector EXTVERSION = 0.6.2 -OBJS = src\bitvector.obj src\halfutils.obj src\halfvec.obj src\hnsw.obj src\hnswbuild.obj src\hnswinsert.obj src\hnswscan.obj src\hnswutils.obj src\hnswvacuum.obj src\ivfbuild.obj src\ivfflat.obj src\ivfinsert.obj src\ivfkmeans.obj src\ivfscan.obj src\ivfutils.obj src\ivfvacuum.obj src\sparsevec.obj src\vector.obj +OBJS = src\bitutils.obj src\bitvector.obj src\halfutils.obj src\halfvec.obj src\hnsw.obj src\hnswbuild.obj src\hnswinsert.obj src\hnswscan.obj src\hnswutils.obj src\hnswvacuum.obj src\ivfbuild.obj src\ivfflat.obj src\ivfinsert.obj src\ivfkmeans.obj src\ivfscan.obj src\ivfutils.obj src\ivfvacuum.obj src\sparsevec.obj src\vector.obj HEADERS = src\halfvec.h src\sparsevec.h src\vector.h REGRESS = bit_functions btree_halfvec btree_sparsevec btree_vector cast copy halfvec_functions halfvec_input hnsw_bit_hamming hnsw_bit_jaccard hnsw_halfvec_cosine hnsw_halfvec_ip hnsw_halfvec_l2 hnsw_options hnsw_sparsevec_cosine hnsw_sparsevec_ip hnsw_sparsevec_l2 hnsw_unlogged hnsw_vector_cosine hnsw_vector_ip hnsw_vector_l2 ivfflat_halfvec_cosine ivfflat_halfvec_ip ivfflat_halfvec_l2 ivfflat_options ivfflat_unlogged ivfflat_vector_cosine ivfflat_vector_ip ivfflat_vector_l2 sparsevec_functions sparsevec_input vector_functions vector_input diff --git a/src/bitutils.c b/src/bitutils.c new file mode 100644 index 0000000..3025d7c --- /dev/null +++ b/src/bitutils.c @@ -0,0 +1,217 @@ +#include "postgres.h" + +#include "bitutils.h" +#include "port/pg_bitutils.h" + +#ifdef BIT_DISPATCH +#include + +#if defined(HAVE__GET_CPUID) +#include +#else +#include +#endif + +#ifdef _MSC_VER +#define TARGET_AVX512_POPCOUNT +#else +#define TARGET_AVX512_POPCOUNT __attribute__((target("avx512f,avx512vpopcntdq"))) +#endif +#endif + +/* Disable for LLVM due to crash with bitcode generation */ +#if defined(USE_TARGET_CLONES) && !defined(__POPCNT__) && !defined(__llvm__) +#define BIT_TARGET_CLONES __attribute__((target_clones("default", "popcnt"))) +#else +#define BIT_TARGET_CLONES +#endif + +/* Use built-ins when possible for inlining */ +#if defined(HAVE__BUILTIN_POPCOUNT) && defined(HAVE_LONG_INT_64) +#define popcount64(x) __builtin_popcountl(x) +#elif defined(HAVE__BUILTIN_POPCOUNT) && defined(HAVE_LONG_LONG_INT_64) +#define popcount64(x) __builtin_popcountll(x) +#elif !defined(_MSC_VER) +/* Fails to resolve with MSVC */ +#define popcount64(x) pg_popcount64(x) +#endif + +uint64 (*BitHammingDistance) (uint32 bytes, unsigned char *ax, unsigned char *bx, uint64 distance); +double (*BitJaccardDistance) (uint32 bytes, unsigned char *ax, unsigned char *bx, uint64 ab, uint64 aa, uint64 bb); + +BIT_TARGET_CLONES static uint64 +BitHammingDistanceDefault(uint32 bytes, unsigned char *ax, unsigned char *bx, uint64 distance) +{ +#ifdef popcount64 + for (; bytes >= sizeof(uint64); bytes -= sizeof(uint64)) + { + uint64 axs; + uint64 bxs; + + /* Ensure aligned */ + memcpy(&axs, ax, sizeof(uint64)); + memcpy(&bxs, bx, sizeof(uint64)); + + distance += popcount64(axs ^ bxs); + + ax += sizeof(uint64); + bx += sizeof(uint64); + } +#endif + + for (uint32 i = 0; i < bytes; i++) + distance += pg_number_of_ones[ax[i] ^ bx[i]]; + + return distance; +} + +#ifdef BIT_DISPATCH +TARGET_AVX512_POPCOUNT static uint64 +BitHammingDistanceAvx512Popcount(uint32 bytes, unsigned char *ax, unsigned char *bx, uint64 distance) +{ + __m512i dist = _mm512_setzero_si512(); + + for (; bytes >= sizeof(__m512i); bytes -= sizeof(__m512i)) + { + __m512i axs = _mm512_loadu_si512((const __m512i *) ax); + __m512i bxs = _mm512_loadu_si512((const __m512i *) bx); + + dist = _mm512_add_epi64(dist, _mm512_popcnt_epi64(_mm512_xor_si512(axs, bxs))); + + ax += sizeof(__m512i); + bx += sizeof(__m512i); + } + + distance += _mm512_reduce_add_epi64(dist); + + return BitHammingDistanceDefault(bytes, ax, bx, distance); +} +#endif + +BIT_TARGET_CLONES static double +BitJaccardDistanceDefault(uint32 bytes, unsigned char *ax, unsigned char *bx, uint64 ab, uint64 aa, uint64 bb) +{ +#ifdef popcount64 + for (; bytes >= sizeof(uint64); bytes -= sizeof(uint64)) + { + uint64 axs; + uint64 bxs; + + /* Ensure aligned */ + memcpy(&axs, ax, sizeof(uint64)); + memcpy(&bxs, bx, sizeof(uint64)); + + ab += popcount64(axs & bxs); + aa += popcount64(axs); + bb += popcount64(bxs); + + ax += sizeof(uint64); + bx += sizeof(uint64); + } +#endif + + for (uint32 i = 0; i < bytes; i++) + { + ab += pg_number_of_ones[ax[i] & bx[i]]; + aa += pg_number_of_ones[ax[i]]; + bb += pg_number_of_ones[bx[i]]; + } + + if (ab == 0) + return 1; + else + return 1 - (ab / ((double) (aa + bb - ab))); +} + +#ifdef BIT_DISPATCH +TARGET_AVX512_POPCOUNT static double +BitJaccardDistanceAvx512Popcount(uint32 bytes, unsigned char *ax, unsigned char *bx, uint64 ab, uint64 aa, uint64 bb) +{ + __m512i abx = _mm512_setzero_si512(); + __m512i aax = _mm512_setzero_si512(); + __m512i bbx = _mm512_setzero_si512(); + + for (; bytes >= sizeof(__m512i); bytes -= sizeof(__m512i)) + { + __m512i axs = _mm512_loadu_si512((const __m512i *) ax); + __m512i bxs = _mm512_loadu_si512((const __m512i *) bx); + + abx = _mm512_add_epi64(abx, _mm512_popcnt_epi64(_mm512_and_si512(axs, bxs))); + aax = _mm512_add_epi64(aax, _mm512_popcnt_epi64(axs)); + bbx = _mm512_add_epi64(bbx, _mm512_popcnt_epi64(bxs)); + + ax += sizeof(__m512i); + bx += sizeof(__m512i); + } + + ab += _mm512_reduce_add_epi64(abx); + aa += _mm512_reduce_add_epi64(aax); + bb += _mm512_reduce_add_epi64(bbx); + + return BitJaccardDistanceDefault(bytes, ax, bx, ab, aa, bb); +} +#endif + +#ifdef BIT_DISPATCH +#define CPU_FEATURE_OSXSAVE (1 << 27) /* F1 ECX */ +#define CPU_FEATURE_AVX512F (1 << 16) /* F7,0 EBX */ +#define CPU_FEATURE_AVX512VPOPCNTDQ (1 << 14) /* F7,0 ECX */ + +#ifdef _MSC_VER +#define TARGET_XSAVE +#else +#define TARGET_XSAVE __attribute__((target("xsave"))) +#endif + +TARGET_XSAVE static bool +SupportsAvx512Popcount() +{ + unsigned int exx[4] = {0, 0, 0, 0}; + +#if defined(HAVE__GET_CPUID) + __get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]); +#else + __cpuid(exx, 1); +#endif + + /* Check OS supports XSAVE */ + if ((exx[2] & CPU_FEATURE_OSXSAVE) != CPU_FEATURE_OSXSAVE) + return false; + + /* Check XMM, YMM, and ZMM registers are enabled */ + if ((_xgetbv(0) & 0xe6) != 0xe6) + return false; + +#if defined(HAVE__GET_CPUID) + __get_cpuid_count(7, 0, &exx[0], &exx[1], &exx[2], &exx[3]); +#else + __cpuidex(exx, 7, 0); +#endif + + /* Check AVX512F */ + if ((exx[1] & CPU_FEATURE_AVX512F) != CPU_FEATURE_AVX512F) + return false; + + /* Check AVX512VPOPCNTDQ */ + return (exx[2] & CPU_FEATURE_AVX512VPOPCNTDQ) == CPU_FEATURE_AVX512VPOPCNTDQ; +} +#endif + +void +BitvecInit(void) +{ + /* + * Could skip pointer when single function, but no difference in + * performance + */ + BitHammingDistance = BitHammingDistanceDefault; + BitJaccardDistance = BitJaccardDistanceDefault; + +#ifdef BIT_DISPATCH + if (SupportsAvx512Popcount()) + { + BitHammingDistance = BitHammingDistanceAvx512Popcount; + BitJaccardDistance = BitJaccardDistanceAvx512Popcount; + } +#endif +} diff --git a/src/bitutils.h b/src/bitutils.h new file mode 100644 index 0000000..10fa06d --- /dev/null +++ b/src/bitutils.h @@ -0,0 +1,23 @@ +#ifndef BITUTILS_H +#define BITUTILS_H + +/* Only enable for more recent compilers */ +#if defined(__x86_64__) && defined(__GNUC__) && __GNUC__ >= 8 +#define BIT_DISPATCH +#elif defined(__x86_64__) && defined(__clang_major__) && __clang_major__ >= 7 +#define BIT_DISPATCH +#elif defined(_M_AMD64) && defined(_MSC_VER) && _MSC_VER >= 1920 +#define BIT_DISPATCH +#endif + +/* target_clones requires glibc */ +#if defined(BIT_DISPATCH) && defined(__gnu_linux__) +#define USE_TARGET_CLONES +#endif + +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); + +void BitvecInit(void); + +#endif diff --git a/src/bitvector.c b/src/bitvector.c index 3ecea0f..cf73eda 100644 --- a/src/bitvector.c +++ b/src/bitvector.c @@ -1,7 +1,7 @@ #include "postgres.h" +#include "bitutils.h" #include "bitvector.h" -#include "port/pg_bitutils.h" #include "utils/varbit.h" #if PG_VERSION_NUM >= 160000 @@ -46,17 +46,10 @@ hamming_distance(PG_FUNCTION_ARGS) { VarBit *a = PG_GETARG_VARBIT_P(0); VarBit *b = PG_GETARG_VARBIT_P(1); - unsigned char *ax = VARBITS(a); - unsigned char *bx = VARBITS(b); - uint64 distance = 0; CheckDims(a, b); - /* TODO Improve performance */ - for (uint32 i = 0; i < VARBITBYTES(a); i++) - distance += pg_number_of_ones[ax[i] ^ bx[i]]; - - PG_RETURN_FLOAT8((double) distance); + PG_RETURN_FLOAT8((double) BitHammingDistance(VARBITBYTES(a), VARBITS(a), VARBITS(b), 0)); } /* @@ -68,23 +61,8 @@ jaccard_distance(PG_FUNCTION_ARGS) { VarBit *a = PG_GETARG_VARBIT_P(0); VarBit *b = PG_GETARG_VARBIT_P(1); - unsigned char *ax = VARBITS(a); - unsigned char *bx = VARBITS(b); - uint64 ab = 0; - uint64 aa; - uint64 bb; CheckDims(a, b); - /* TODO Improve performance */ - for (uint32 i = 0; i < VARBITBYTES(a); i++) - ab += pg_number_of_ones[ax[i] & bx[i]]; - - if (ab == 0) - PG_RETURN_FLOAT8(1); - - aa = pg_popcount((char *) ax, VARBITBYTES(a)); - bb = pg_popcount((char *) bx, VARBITBYTES(b)); - - PG_RETURN_FLOAT8(1 - (ab / ((double) (aa + bb - ab)))); + PG_RETURN_FLOAT8(BitJaccardDistance(VARBITBYTES(a), VARBITS(a), VARBITS(b), 0, 0, 0)); } diff --git a/src/vector.c b/src/vector.c index 2bb6bee..a3e0132 100644 --- a/src/vector.c +++ b/src/vector.c @@ -2,6 +2,7 @@ #include +#include "bitutils.h" #include "bitvector.h" #include "catalog/pg_type.h" #include "common/shortest_dec.h" @@ -56,6 +57,7 @@ PGDLLEXPORT void _PG_init(void); void _PG_init(void) { + BitvecInit(); HalfvecInit(); HnswInit(); IvfflatInit(); diff --git a/test/expected/bit_functions.out b/test/expected/bit_functions.out index e512da1..a9ba472 100644 --- a/test/expected/bit_functions.out +++ b/test/expected/bit_functions.out @@ -28,6 +28,24 @@ SELECT hamming_distance('10101010101010101010', '01010101010101010101'); 20 (1 row) +SELECT hamming_distance('101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101', '101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101'); + hamming_distance +------------------ + 0 +(1 row) + +SELECT hamming_distance('101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101', '010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010'); + hamming_distance +------------------ + 513 +(1 row) + +SELECT hamming_distance('110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011', '100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001'); + hamming_distance +------------------ + 2 +(1 row) + SELECT hamming_distance('', ''); hamming_distance ------------------ @@ -86,6 +104,24 @@ SELECT jaccard_distance('10101010101010101010', '01010101010101010101'); 1 (1 row) +SELECT jaccard_distance('101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101', '101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101'); + jaccard_distance +------------------ + 0 +(1 row) + +SELECT jaccard_distance('101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101', '010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010'); + jaccard_distance +------------------ + 1 +(1 row) + +SELECT jaccard_distance('110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011', '100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001'); + jaccard_distance +------------------ + 0.5 +(1 row) + SELECT jaccard_distance('', ''); jaccard_distance ------------------ diff --git a/test/sql/bit_functions.sql b/test/sql/bit_functions.sql index abec32f..eee9ce6 100644 --- a/test/sql/bit_functions.sql +++ b/test/sql/bit_functions.sql @@ -3,6 +3,9 @@ SELECT hamming_distance('111', '110'); SELECT hamming_distance('111', '100'); SELECT hamming_distance('111', '000'); SELECT hamming_distance('10101010101010101010', '01010101010101010101'); +SELECT hamming_distance('101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101', '101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101'); +SELECT hamming_distance('101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101', '010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010'); +SELECT hamming_distance('110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011', '100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001'); SELECT hamming_distance('', ''); SELECT hamming_distance('111', '00'); SELECT hamming_distance('111', '000'::varbit(4)); @@ -15,6 +18,9 @@ SELECT jaccard_distance('1111', '1000'); SELECT jaccard_distance('1111', '0000'); SELECT jaccard_distance('1100', '1000'); SELECT jaccard_distance('10101010101010101010', '01010101010101010101'); +SELECT jaccard_distance('101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101', '101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101'); +SELECT jaccard_distance('101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101', '010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010'); +SELECT jaccard_distance('110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011', '100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001'); SELECT jaccard_distance('', ''); SELECT jaccard_distance('1111', '000'); SELECT jaccard_distance('1111', '0000'::varbit(5));