惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

人人都是产品经理
人人都是产品经理
有赞技术团队
有赞技术团队
L
LangChain Blog
C
Check Point Blog
博客园 - 【当耐特】
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
V2EX
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
GbyAI
GbyAI
美团技术团队
博客园 - 司徒正美
Google DeepMind News
Google DeepMind News
WordPress大学
WordPress大学
aimingoo的专栏
aimingoo的专栏
S
SegmentFault 最新的问题
A
About on SuperTechFans
Blog — PlanetScale
Blog — PlanetScale
Hugging Face - Blog
Hugging Face - Blog
博客园 - 叶小钗
腾讯CDC
B
Blog
G
Google Developers Blog
The Cloudflare Blog
P
Proofpoint News Feed

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания
Насколько многомерным может быть многомерный точный индекс?
zzeng · 2026-05-18 · via Все публикации подряд на Хабре

Насколько многомерным может быть многомерный точный индекс?

Уровень сложностиСредний

Время на прочтение7 мин

Охват и читатели7

Вот, например, Milvius(DiskANN) рассчитан на вектора размерности до 32 768, но это приближенный поиск.  Но как насчёт поиска точного?

В данной статье рассматривается работоспособность 1024 мерного индекса, хранилищем которого служит обычное B-дерево (насколько вообще может быть обычным такое дерево). Используемый диск - вполне себе “железный” старый добрый WD Purple, оперативная память сознательно ограничена 8 Гб. Можно ли что-то из этого выжать на рядовом десктопе за приемлемое время?

Данная работа опирается на ранее описанный алгоритм:
Презентация алгоритма
3D и очевидные оптимизации
8D и перспективы в OLAP
3D на сфере
Как насчет индексации неточечных объектов?
А что с кривой Гильберта ?
Заметающие кривые
Логический (одноразрядный) многомерный индекс
Исходники

Задача

Удачно выбранный пример - половина успеха.

  • индексируем 1024-мерные точки. Этого довольно (с точки зрения автора) чтобы прикоснуться к прелестям многомерных пространств. Но недостаточно много, чтобы вся затея превратилась в сплошное занудство. Принципиальное ограничение - на одной странице дерева должно помещаться два и более элемента, иначе, какое же это дерево. Так что, требуется больше размерность - увеличиваем размер страницы.

  • точка устроена так: (sin(x), sin(2*x),...,sin(1024*x)) где x принимает значения от 0 до 2𝛑 с шагом 0.000002𝛑, всего миллион значений. Миллиона довольно, чтобы почувствовать как устроена задача, но недостаточно …

  • значения 8-разрядные, сдвинуты вверх так, чтобы заметать диапазон [0…255]. Автор экспериментировал и с 16-разрядными значениями, принципиально ничего не меняется, разница лишь в том, сколько элементов помещается на странице

  • используется кодирование Z-Order

  • после кодирования точка превращается в 8192-разрядное целое число

  • в индексе это число выглядит как ключ из 128 беззнаковых 64 разрядных целых чисел

  • экспериментируем с размерами страниц от 8 до 64K

  • при индексировании применяется сжатие, но для такого рода данных оно мало что даёт

Образец данных в текстовом виде: сначала номер строки, затем 1024 значения

#                                                                                                                                                              1 , 128, 128, 128, … 128

590383 , 60, 243, 3, 225, 91, 95, 222, … 245, 4, 220, 98, 88

1000000 , 128, 128, 128, … 128

Индексация

При построении индекса каждая точка превращается в 8192 разрядное число, которое заносится в индекс самым обычным образом. Многомерный поиск заключен именно в поиске а не в индексации.

Что касается реализации B-дерева, в ней нет ничего необычного, вполне заурядное проприетарное дерево. Нет ничего проще, чем написать своё B-дерево, я сам это делал неоднократно.

  • Время индексации (8к страницы): 12’6’’, для остальных страниц примерно  то же

  • Пиковое потребление памяти: ~2 Гб, управляется выделенным пулом страниц

  • Размер индекса: ~1.25Гб, для всех типов страниц, оно и понятно, объем информации не меняется

  • или ~ 156 000 страниц по 8К 

  • в среднем число элементов на такой странице: 6.4
    т.е. сжатия практически нет.

Глянем одним глазком что насоздавали

Совместное представление значений в разных размерностях показано ниже.

Фиг.1 совместное распределение колонок 100, 200 и 300

Фиг.1 совместное распределение колонок 100, 200 и 300

Фиг.2 совместное распределение колонок 8, 18 и 28

Фиг.2 совместное распределение колонок 8, 18 и 28

Фиг.3 совместное распределение колонок 13, 31 и 101

Фиг.3 совместное распределение колонок 13, 31 и 101

Поиск в окрестности

Проверим работоспособность индекса, во первых, при поиске на точное совпадение, во вторых, в окрестности искомой точки.

Будем брать произвольные вектора (3 штуки - с номерами 590383, 117806, 711480) из исходных данных, делать поиск вокруг них в расширяющейся окрестности по каждой размерности. Если размер окрестности нулевой, это точное совпадение.

Результатом будет количество прочитанных страниц

из холодного состояния

и число полученных результатов. Холодное состояние подразумевает отсутствие страниц в кэше и их распаковку при чтении. Но диск всё равно “разогретый” так что собственно физическое чтение не учитывается, для “охлаждения” диска потребуется индекс потолще, размером побольше дискового кэша ОС.

Три точки, конечно не слишком представительная выборка, тем не менее этого достаточно, чтобы получить общее представление о задаче, не впадая в уныние от необходимости миллион раз перезапускать сервер.

название

время, мс
8/16/32/64K страницы

число прочитанных страниц (8/16/32/64K)

к-во результатов


точное совпадение

4 / 4 / 5 / 4
4 / 4 / 5 / 5
4 / 5 / 5 / 4

6 / 4 / 3 / 3
5 / 4 / 3 / 3
6 / 4 / 3 / 3

1
1
1


окрестность ± 1

27 / 5 / 3 / 4
20 / 5 / 6 / 4
21 / 5 / 7 / 5

9 / 6 / 5 / 4
6 / 4 / 3 / 3
8 / 6 / 4 / 4

3
3
3


окрестность ± 2

18 / 28 / 6 / 2
34 / 5 / 7 / 5
25 / 10 / 11 / 11

9 / 6  / 4 / 4
6 / 5  / 4 / 3
17 / 15 / 10 / 8

5
5
5


окрестность ± 4

24 / 49 / 7 / 2
29 / 51 / 22 / 4
16 / 5 /13 / 27

11 / 7 / 5 / 5
8 / 6 / 4 / 4
20 / 15 / 10 / 8

11
11
11


окрестность ± 8

69 / 49 /52 / 24
25 / 54 / 47 / 9
57 / 56 / 16 / 19

15 / 10 / 8 / 7
17 / 11 / 8 / 5
20 / 15 / 10 / 8

21
21
21


окрестность ± 16

80 / 71 / 47 / 52
79 / 57 / 52 / 28
63 / 48 / 24 / 47

37 / 22 / 14 / 11
77 / 47 / 33 / 26
50 / 34 / 20 / 14

41
41
41


окрестность ±32

124 / 81 / 76 / 63
125 / 85 / 60 / 40
116 / 75 / 42 / 53

146 / 95 / 61 / 40
108 / 66 / 37 / 27
105 / 68 / 44 / 30

82
81
82


окрестность ±64

255 / 143 / 126 / 71
242 / 164 / 114 / 114
206 / 175 / 97 / 143

413 / 259 / 168 / 114
378 / 226 / 140 / 96
427 / 262 / 180 / 126

162
164
163


окрестность ±96

368 / 177 / 180 / 150
342 / 244 / 175 / 157
312 / 225 / 157 / 151

927 / 573 / 376 / 268
904 / 539 / 361 / 255
857 / 523 / 341 / 240

246
247
246


окрестность ±128

2’28’’ / 1’21’’ / 48’’ / 31’’
2’29’’ / 1’22’’ / 46‘’ / 31’’
2’26’’ / 1’23’’ / 50’’ / 30’’

152995 / 70416 / 33162 / 16112
152938 / 70400 / 33159 / 16110
152991 / 70415 / 33162 / 16111

349
350
348

Таб.1  Число прочитанных страниц и найденных результатов в расширяющейся окрестности выбранных точек.

Фиг.4 данные из Таб.1 в графическом представлении  X-радиус Y-число страниц

Фиг.4 данные из Таб.1 в графическом представлении X-радиус Y-число страниц

Вызывает недоумение, что поиск с радиусом в 128 (т.е. во всю область значений) находит всего лишь несколько сотен значений из миллиона. Это одна из особенностей высоких размерностей. Например, если окрестность строится от значения 100, то она получится [0…228], т.е. некоторая фильтрация всё-таки произойдёт. А поскольку размерностей много, в результате отсеется почти всё. На самом деле, удивительно, что вообще хоть что-то нашлось.

Дело в данных. Младшая часть гармоник изменяется очень медленно. Старшие гармоники меняются примерно со скоростью расширения окрестности. Если размер окрестности равен 3, в ней окажутся сама центральная точка и две её соседки.
Это не специально, так получилось.

Поиск по частично неизвестным данным

Будем использовать те же модельные данные и те же три вектора (590383, 117806, 711480) для поиска в ситуации, когда часть координат(размерностей) не заполнена. В случае когда какая-та из координат считается неизвестной, для неё подставляется максимально возможный диапазон - в данном случае [0…255].
Неизвестными будем считать значения с начала вектора.
Поиск идёт на точное совпадение известной части вектора.

название

время, мс
8/16/32/64K страницы

число прочитанных 8/16/32/64K страниц

к-во результатов


полные вектора  (1024)

4 / 4 / 5 / 4
4 / 4 / 5 / 5
4 / 5 / 5 / 4

6 / 4 / 3 / 3
5 / 4 / 3 / 3
6 / 4 / 3 / 3

1
1
1


1024 - 100 => 924

2 / 2 / 6 / 4
4 / 1 / 6 / 5
2 / 4 / 3 / 3

6 / 4 / 3 / 3
5 / 4 / 3 / 3
6 / 4 / 3 / 3

1
1
1


1024 - 200 => 824

1 / 4 / 7 / 2
4 / 4 /1 / 2
4 / 4 / 5 / 4

6 / 4 / 3 / 3

5 / 4 / 3 / 3

6 / 4 / 3 / 3

1

1

1


1024 - 400 => 624

3 / 4 / 5 / 6
1 / 4 / 2 / 5
4 / 4 / 6 / 1

6 / 4 / 3 / 3
5 / 4 / 3 / 3

6 / 4 / 3 / 3

1

1

1


1024 - 800 => 224

1 / 5 / 5 / 2
2 / 3 / 4 / 2
4 / 6 / 2 / 1

6 / 4 / 3 / 3 

5 / 4 / 3 / 3

6 / 4 / 3 / 3

1

1

1


1024 - 1000 => 24

14 / 11 / 11 / 9
9 / 12 / 3 / 9
4 / 14 / 19 / 10

41 / 21 / 11 / 7
43 / 22 / 11 / 7
60 / 29 / 15 / 8

1
1
1


1024 - 1012 => 12

52 / 52 / 18 / 28
69 / 71 / 39 / 41
65 / 41 / 52 / 43

379 / 173 / 83 / 42
625 / 287 / 135 / 67
518 / 240 /113 / 65

1
1
1


1024 - 1018 => 6

146 / 73 / 74 / 81
178 / 155 / 96 / 82
328 / 229 / 148 / 104

1848 / 848 / 399 / 196
3257 / 1497 / 703 / 342
5059 / 2317 / 1089 / 530 

1
1
1


1024 - 1021 => 3

2058 / 1771 / 1593 / 1523
2032 / 1730 / 1799 / 1485
2085 / 1793 / 1565 / 1512

19136 / 8777 / 4120 / 2000
19135 / 8777 / 4120 / 2000
19135 / 8777 / 4120 / 2000

3
3
3

Таб.2 количество прочитанных страниц в зависимости от известной части точки (вектора)

Фиг.5 данные Таб.2 в графическом представлении  X-известная часть вектора Y-число страниц

Фиг.5 данные Таб.2 в графическом представлении X-известная часть вектора Y-число страниц

Кажется совершенно невероятным, что при всего 6 известных координатах точки (и 1018 неизвестных) в индексе нашелся единственный результат. Никакого чуда нет. Старшие координаты соответствуют самым высокочастотным гармоникам и комбинация из нескольких последних координат фактически является случайным числом с равномерным распределением. Поскольку в индексе всего миллион векторов (20 разрядов), а в одном значении - 8, часть вектора, делающая его уникальным должна быть длиннее 3.

В догонку совместное распределение числа прочитанных страниц и времени поиска 

Фиг.6 X-время в мсек, Y- число прочитанных 8К страниц

Фиг.6 X-время в мсек, Y- число прочитанных 8К страниц

Время поиска состоит (в нашем случае) из двух факторов - распаковка страниц, стоимость которой примерно одинакова для всех страниц и обработка внутренних подзапросов, сложность которой зависит от того, насколько “удачно” искомая точка легла в индекс.

Итого

Мы познакомились с 1024-х мерным поиском, но основной вопрос пока остаётся без ответа. Действительно, насколько он может быть многомерным?

Всё зависит от размера страниц. 8К страницы допускают с некоторым запасом 1024 размерности. Но мы потренировались и на 64К страницах, значит ли это, что можно говорить о 8192 размерном индексе?

Да, вполне. Более того, не запрещено использовать и страницы большего размера. Наоборот, мы видели, что чем большие страницы используются, тем быстрее идёт поиск.
Но есть же какие-то ограничения?

Разумеется. Во первых, чем больше страницы, тем дороже становятся изменения такого индекса. Одно дело перекомпоновать 8К страницу и совсем другое, когда в ней 256К. Дороже станет и создание индекса, ведь оно подразумевает (кроме тех случаев, когда всё можно отсортировать в памяти) сортировку и произвольную вставку в дерево.

Выше обращалось внимание на то, что значительная часть времени при поиске тратится на генерацию и обработку подзапросов. Этот процесс останавливается, когда текущий подзапрос целиком оказывается на одной листовой странице B-дерева. В этом случае данная страница просто просматривается целиком. Т.е. чем больше страницы, тем меньше будет порождено подзапросов.

Но есть и другая сторона. Если страница большая, а запросы не очень, значительная часть данных загружается, просматривается и отфильтровывается впустую. Так что это самобалансирующийся процесс. Мы видим, что время поиска в 32 и 64К страницах очень близко. 

На данный момент автор рассматривает 64К страницы как оптимальные (для конкретно этой задачи). Так что вот ответ - 8К - вот число размерностей для векторов, с которым  можно будет работать относительно комфортно. Хотя, технически допустимо сделать и намного больше.

PS: спасибо Олегу Бартунову (@zen), сподвигшему меня заняться всем этим.

PPS: титульная картинка взята из к.ф. “Интерстеллар”