Commit Graph

79 Commits

Author SHA1 Message Date
Sergei Golubchik
6e8dbb9693 Merge branch '12.0' into 12.1
Some checks failed
Build on Windows ARM64 / build (push) Has been cancelled
wsrep.wsrep_off: update the result file after c4cad8d50c
2025-08-03 15:01:09 +02:00
Sergei Golubchik
9d3af2c8e3 MDEV-37063 Sporadic segmentation faults possibly related to vector search
Some checks are pending
Build on Windows ARM64 / build (push) Waiting to run
Search_context is supposed to increase the refcnt of the context
it keeps the pointer to. It was doing so for MHNSW_Share, but not
for MHNSW_Trx that was created inside mhnsw_read_next().

This meant MHNSW_Trx could've been reset (when it overflowed),
even though Search_context still had results inside it.
2025-07-21 20:24:34 +02:00
Sergei Golubchik
9ef20acfbe MDEV-37068 Can't find record in 't1' on INSERT to Vector table
ER_DUP_ENTRY only rolls back one current statement, it doesn't
abort the transaction. Thus MHNSW_Trx has to register for
"statement transaction" and support "statement rollback".
2025-07-21 10:24:14 +02:00
Sergei Golubchik
a4f17dc64f cleanup: mhnsw - always scale>0 2025-07-21 10:24:14 +02:00
Sergei Golubchik
d0c6889149 MDEV-37055 UBSAN: 32801 is outside the range of representable values of type 'short'
avoid overflows caused by float precision loss
2025-07-21 10:24:14 +02:00
Alessandro Vetere
7733d63116 MDEV-36758: always release ctx in mhnsw_delete_all
Fixes leak of shared context.
2025-07-07 08:19:13 +00:00
Manjul Mohan
311171c176 MDEV-37107 - Optimise dot_product by loop-unrolling by a factor of 4
This patch introduces loop unrolling by a factor of 4 in the
dot_product() function used in vector-based distance calculations.

The goal is to improve SIMD utilization and overall performance during
high-throughput vector operations, particularly in indexing and search
routines that rely on this function.

Observations from benchmarking (ann-benchmark):
- Query Performance (QPS) improved by 4–10% across datasets.
- Indexation time reduced by 22–28%.
- Loop unrolling factors of 8 or 16 yielded similar performance to
  factor-4 but made the code less readable. Hence, a factor of 4 was
  chosen to maintain a balance between performance and code clarity.

This change is architecture-specific (PowerPC) and should not
introduce any behavioral regressions or side effects in unrelated
parts of the codebase.

Signed-off-by: Manjul Mohan <manjul.mohan@ibm.com>
2025-07-02 00:12:46 +04:00
Sergei Golubchik
c06a25e495 MDEV-36205 autodetection of subdist applicability
* skip subdist optimization for vectors shorter than 2*192, as before

* for longer vectors do not enable the optimization at first, but
  still calculate the extrapolated distance and accumulate statistics
  of extrapolated_distance/true_distance ratios.

  * calculated for every distance_greater_than() call
  * accumulated using parallel Welford's online algorithm,
    only M1 and M2, experiments show that higher-order central
    moment are not needed here
  * stored in MHNSW_Share

* after accumulating 10000 (best by test) data points, enable
  subdist optimization if the standard deviation of ratios (as above)
  is below 0.05, this distinguishes applicable datasets from
  not applicable datasets with a >99.9% probabilty

* to be on the safe side, keep calculating statistics (it's cheap)
  and disable subdist optimization again if standard deviation
  grows above 0.05
2025-07-27 14:02:41 +02:00
Sergei Golubchik
4ca9fca4f6 MDEV-36205 subdist optimization
* in addition to X.distance_to(Y) operation, introduce
  X.distance_greater_than(Y,Z) to be used like

  - if (X.distance_to(Y) > Z)
  + if (X.distance_greater_than(Y,Z) > Z)

  here we can afford to calculate the distance approximately,
  as long as it's greater than Z

* calculate the distance approximately, by looking only at the
  first 192 dimensions and extrapolate

* if it's larger than Z*1.05 (safety margin), assume |X-Y| > Z too
  and just return the extrapolated distance

* 192 and 1.05 are best by test

* optimization is obviously not applicable for vectors shorter than
  192 dimensions, let's not apply it for vectors shorter than 2*192
  dimensions because it needs to stop SIMD calculations and
  calculate the intermediate distance, this is not cheap

* this works reasonably well for dbpedia-openai and for randomized
  (all vectors multiplied by a random orthogonal matrix) variants
  of gist and *mnist, but completely destroys recall for
  original (non-randomized) gist and *mnist.

* up to 50% speedup for dbpedia-openai-500k, 40% faster run for gist-960
2025-07-27 14:02:41 +02:00
Sergei Golubchik
ca19a25105 MDEV-35897 cleanup: Stats structure
don't abuse TABLE::used_stat_records, get a dedicated
structure Stats for maintaining various graph-related
runtime statistics, and keep it in MHNSW_Share

also, move diameter and ef_power there
2025-07-27 14:02:41 +02:00
Sergei Golubchik
c2bff9ff70 cleanup: MHNSW_param
put common arguments of many functions into one "param" structure,
instead of passing them every time independently
2025-07-27 14:02:41 +02:00
Sergei Golubchik
84987d1471 cleanup 2025-07-27 14:02:41 +02:00
Sergey Vojtovich
02eb8f7246 MDL_lock encapsulation: MDL_lock::get_key()
Avoid accessing MDL_lock::key from outside of MDL_lock class directly,
use MDL_lock::get_key() instead.

This is part of broader cleanup, which aims to make large part of
MDL_lock members private. It is needed to simplify further work on
MDEV-19749 - MDL scalability regression after backup locks.
2025-07-15 23:19:06 +04:00
Sergei Golubchik
82867e07e3 MDEV-35897 vector index search allocates too much memory for large ef_search
never estimate that a graph search will visit more nodes than there
are in the graph. In fact, let's reduce the graph size by 30%, it'll
increase the false positive rate of a bloom filter by 2% when
visiting the whole graph, it doesn't affect recall noticeably.

we need to read the shared graph size under a lock. let's store it
in the thread-local unused TABLE::used_stat_records member.
2025-04-25 16:00:37 +02:00
Sergei Golubchik
1691b0cfac cleanup: mhnsw, fix vector length when cosine
formulas use half the distance, so the correct normalized
length is 0.5.

This change doesn't affect search results.
2025-04-18 09:41:24 +02:00
Sergei Golubchik
7db60533c7 MDEV-36188 assert with vector index and very long PK
limit PK length if the table has a vector index
2025-04-18 09:41:23 +02:00
Manjul Mohan
6bb92f98ce MDEV-36184 - mhnsw: support powerpc64 SIMD instructions
This patch optimises the dot_product function by leveraging
vectorisation through SIMD intrinsics. This transformation enables
parallel execution of multiple operations, significantly improving the
performance of dot product computation on supported architectures.

The original dot_product function does undergo auto-vectorisation when
compiled with -O3. However, performance analysis has shown that the
newly optimised implementation performs better on Power10 and achieves
comparable performance on Power9 machines.

Benchmark tests were conducted on both Power9 and Power10 machines,
comparing the time taken by the original (auto-vectorized) code and the
new vectorised code. GCC 11.5.0 on RHEL 9.5 operating system with -O3
were used. The benchmarks were performed using a sample test code with
a vector size of 4096 and 10⁷ loop iterations. Here are the average
execution times (in seconds) over multiple runs:

Power9:
Before change: ~16.364 s
After change: ~16.180 s
Performance gain is modest but measurable.

Power10:
Before change: ~8.989 s
After change: ~6.446 s
Significant improvement, roughly 28–30% faster.

Signed-off-by: Manjul Mohan <manjul.mohan@ibm.com>
2025-04-14 18:01:16 +02:00
Sergei Golubchik
9ee09a33bb Merge branch '11.7' into 11.8 2025-02-11 20:29:43 +01:00
Sergei Golubchik
c453391187 MDEV-35834 Server crash in FVector::distance_to upon concurrent SELECT
a start node in the shared context must be atomically assigned
to a valid and fully initialized node otherwise a concurrent
thread might use it before it's safe
2025-02-06 21:47:01 +01:00
Sergei Golubchik
528249a20a cleanup: one Item_func_vec_distance class, not three
prepare for MDEV-35450 VEC_DISTANCE auto-detection
2025-01-21 12:18:56 +01:00
Sergey Vojtovich
11a6c1b30a MDEV-34699 - mhnsw: support aarch64 SIMD instructions
SIMD implementations of bloom filters and dot product calculation.

A microbenchmark shows 1.7x dot product performance improvement compared to
regular -O2/-O3 builds and 2.4x compared to builds with auto-vectorization
disabled.

Performance improvement (microbenchmark) for bloom filters is less exciting,
within 10-30% ballpark depending on compiler options and load.

Misc implementation notes:
CalcHash: no _mm256_shuffle_epi8(), use explicit XOR/shift.
CalcHash: no 64bit multiplication, do scalar multiplication.
ConstructMask/Query: no _mm256_i64gather_epi64, access array elements explicitly.
Query: no _mm256_movemask_epi8, accumulate bits manually.

Closes #3671
2025-01-17 22:56:51 +01:00
Sergei Golubchik
8ef37ade17 MDEV-35745 Assertion failure, ASAN errors, crash in mhnsw_read_first/mhnsw_read_next
If the table has many deleted nodes, they can overflow
`candidates` even when `best` isn't full yet
2025-01-13 19:57:12 +01:00
Marko Mäkelä
33907f9ec6 Merge 11.4 into 11.7 2024-12-02 17:51:17 +02:00
Sergei Golubchik
74743b0d88 fix test failures on x86, gcc -O1
x86 builds don't use SIMD, fast math and inlining causes
distances to be quite unstable and

1) comparison with the threshold no longer works, the distance calculated
   twice between the same two vectors comes out differently

2) a bunch of identical vectors get the non-zero distance between
   them and HNSW cross-links them with no outbound links (if there're
   more than 2M identical vectors). Let's strengthen the select_neighbors
   heuristic to skip neighbors that are too close to each other

MDEV-35418 suggests a better solution for this.
2024-11-14 11:32:08 +01:00
Sergei Golubchik
ebbbe9d960 MDEV-35319 ER_LOCK_DEADLOCK not detected upon DML on table with vector key, server crashes
cannot ignore the error in MHNSW_Share::acquire() - it could be a deadlock
signal, after which no further operations are allowed
2024-11-05 14:00:52 -08:00
Sergei Golubchik
e5a5d2b78d MDEV-35214 Server crashes in FVectorNode::gref_len with insufficient mhnsw_max_cache_size
now with streaming (MDEV-35032) we cannot longer free MHNSW_Trx
at the end of the search. Cannot even free it at the end of the
mhnsw_insert, because there can be a search running (INSERT ... SELECT).

Let's do reference counting, even though it's a thread-local object.
2024-11-05 14:00:52 -08:00
Sergei Golubchik
b09c8b03d7 MDEV-35244 Vector-related system variables could use better names
considering that users don't interact with MariaDB vector search directly,
but primarily use AI frameworks, we should use names familiar
to vector store connector writers and for AI framework users.
That is industry standard M and ef.

mhnsw_cache_size -> mhnsw_max_cache_size
mhnsw_distance_function -> mhnsw_default_distance
mhnsw_max_edges_per_node -> mhnsw_default_m
mhnsw_min_limit -> mhnsw_ef_search

inside CREATE TABLE:
max_edges_per_node -> m
distance_function -> distance
2024-11-05 14:00:52 -08:00
Sergei Golubchik
0b9bc6c3cd MDEV-35246 Vector search skips a row in the table
stronger condition in select_neighbors() to reject exact matches too
2024-11-05 14:00:52 -08:00
Sergei Golubchik
7d081c1b83 MDEV-35223 REPAIR does not fix MyISAM table with vector key after crash recovery
resort to alter for repair too
2024-11-05 14:00:52 -08:00
Sergei Golubchik
926b339b93 MDEV-35194 non-BNL join fails on assertion
with streaming implemened mhnsw no longer needs to know
the LIMIT in advance. let's just cap it to avoid allocating
too much memory for the one step result set
2024-11-05 14:00:52 -08:00
Sergei Golubchik
597e34d000 MDEV-35213 Server crash or assertion failure upon query with high value of mhnsw_min_limit
mhnsw_min_limit must not be larger than candidates queue size
2024-11-05 14:00:52 -08:00
Sergei Golubchik
78119d1ae5 MDEV-33410 VECTOR data type 2024-11-05 14:00:51 -08:00
Sergei Golubchik
eb4ab2ce8f MDEV-35061 XA PREPARE "not supported by the engine" from storage engine mhnsw, memory leak
disallow explicit XA PREPARE over mhnsw indexes
2024-11-05 14:00:51 -08:00
Sergei Golubchik
09cd817f5d MDEV-35060 Assertion failure upon DML on table with vector under lock 2024-11-05 14:00:51 -08:00
Sergei Golubchik
09889d417b MDEV-35055 ASAN errors in TABLE_SHARE::lock_share upon committing transaction after FLUSH on table with vector key
MHNSW_Trx cannot store a pointer to the TABLE_SHARE for the duration
of a transaction, because the share can be evicted from the cache any
time.

Use a pointer to the MDL_ticket instead, it is stored in the THD
and has a duration of MDL_TRANSACTION, so won't go away.

When we need a TABLE_SHARE - on commit - get a new one from tdc.
Normally, it'll be already in the cache, so it'd be fast.
We don't optimize for the edge case when TABLE_SHARE was evicted.
2024-11-05 14:00:51 -08:00
Sergei Golubchik
9f80e3fbb7 MDEV-35032 streaming mode for mhnsw search
support SQL semantics for SELECT ... WHERE ... ORDER BY ... LIMIT

* switch from returning k nearest neighbors to returning
  as many as needed, in k-neighbor chunks, with increasing distance
* make search_layer() skips nodes that are closer than a threshold
* read_next keeps a search context - list of k found nodes,
  threshold, ctx, etc.
* when the list of found nodes is exhausted, it repeats the search
  starting from last found nodes and a threshold
* search context kepts ctx->refcount incremented, so ctx won't go away
* but commit_lock is unlocked between calls, so InnoDB can modify the table
* use ctx version to detect that, switch to MHNSW_Trx when it happens

bugfix:
* use the correct lock in ha_external_lock() for the graph table
* InnoDB didn't reset locks on ha_external_lock(F_UNLCK) and previous
  LOCK_X leaked into the next statement
2024-11-05 14:00:51 -08:00
Sergei Golubchik
b4811a9b63 cleanup: simplify search_layer() usage, remove std::swap() 2024-11-05 14:00:51 -08:00
Sergei Golubchik
7b00e2351b rename MHNSW_Context->MHNSW_Share 2024-11-05 14:00:51 -08:00
Sergei Golubchik
a6499049af MDEV-35033 LeakSanitizer errors in my_malloc / safe_mutex_lazy_init_deadlock_detection / MHNSW_Context::alloc_node and alike
ctx wasn't released on errors
2024-11-05 14:00:51 -08:00
Sergei Golubchik
3c6e836110 generous_furthest optimization
make generosity depend on

1. M. Keep small M's fast, increase generosity for larger M's to get
   better recall.

2. distance. Keep generosity small when vectors are far from the
   target, increase generosity when the search gets closer. This
   allows to examine more relevant vectors but doesn't waste time
   examining irrelevant vectors. Particularly important with cosine
   metric when the distance is bounded
2024-11-05 14:00:50 -08:00
Sergei Golubchik
97b2392ede cleanup: TABLE_SHARE::lock_share() helper
also: renames, s/const/constexpr/ for consistency
2024-11-05 14:00:50 -08:00
Sergey Vojtovich
a90fa3f397 ALTER TABLE fixes for high-level indexes (i)
Fixes for ALTER TABLE ... ADD/DROP COLUMN, ALGORITHM=COPY.

Let quick_rm_table() remove high-level indexes along with original table.

Avoid locking uninitialized LOCK_share for INTERNAL_TMP_TABLEs.

Don't enable bulk insert when altering a table containing vector index.
InnoDB can't handle situation when bulk insert is enabled for one table
but disabled for another. We can't do bulk insert on vector index as it
does table updates currently.
2024-11-05 14:00:50 -08:00
Sergei Golubchik
e826875fe5 AVX-512 support 2024-11-05 14:00:50 -08:00
Sergei Golubchik
2ad9df8c9b VEC_Distance_Cosine() 2024-11-05 14:00:50 -08:00
Sergei Golubchik
2e1fcc6a80 rename VEC_Distance to VEC_Distance_Euclidean
and create a parent Item_func_vec_distance_common class
2024-11-05 14:00:50 -08:00
Sergei Golubchik
0da820cb12 mhnsw: use plugin index options and transaction_participant API 2024-11-05 14:00:50 -08:00
Sergei Golubchik
aed5928207 cleanup: extract transaction-related part of handlerton
into a separate transaction_participant structure

handlerton inherits it, so handlerton itself doesn't change.
but entities that only need to participate in a transaction,
like binlog or online alter log, use a transaction_participant
and no longer need to pretend to be a full-blown but invisible
storage engine which doesn't support create table.
2024-11-05 14:00:50 -08:00
Sergei Golubchik
ebcbed6d74 post-fixes for TRUNCATE
* fix the truncate-by-handler variant, used by InnoDB
* test that insert works after truncate, meaning graph table was emptied
* test that the vector index size is zero after truncate in MyISAM
2024-11-05 14:00:49 -08:00
Sergei Golubchik
26e599cd32 mhnsw: make the search less greedy
introduced a generosity factor that makes the search less greedy.
it dramatically improves the recall by making the search a bit slower
(for the same recall one can use half the M and smaller ef).

had to add Queue::safe_push() method that removes one of the
furthest elements (not necessarily the furthest) in the queue
to keep it from overflowing.
2024-11-05 14:00:49 -08:00
Sergei Golubchik
885eb19823 cleanup search_layer()
to return only as many elements as needed, the caller no longer needs to
overallocate result arrays for throwaway nodes
2024-11-05 14:00:49 -08:00