Files
UniversalContainer/docs/architecture.md
T
Efim Beshmenev aea6e330e3 Realisation v1
2026-08-11 22:19:34 +03:00

103 lines
6.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Архитектура
## Представления
`AdaptiveSequence<T>` хранит `variant<vector<T>, TieredStorage<T>>`. Векторный
режим предназначен для random/sequential read и append. Tiered-режим состоит из
независимо выделенных циклических leaf-блоков (`RingBlock`) и многоуровневого
каталога весов. Каталог строит только необходимое число уровней до заданного
максимума; поиск спускается по уровням, а отдельный Fenwick index восстанавливает
логическую позицию stable ID.
Текущий `TieredStorage` — сегментированный исследовательский вариант, а не
полная реализация implicit tiered vector с offsets на каждом внутреннем узле.
Offsets реально используются внутри leaf, но split/merge меняет массив
дескрипторов leaf и перестраивает каталог. Это важное ограничение: некоторые
uniform-edit результаты отражают O(number_of_leaves) обслуживание split, а не
теоретическую границу полноценного tiered vector.
## Адаптивное решение
Storage и policy разделены. Контейнер передаёт `OperationSample`, policy
возвращает:
```cpp
struct AdaptationDecision {
StorageMode target;
TieredConfig tiered_config;
double expected_saving;
};
```
Для каждого окна оцениваются vector и все tiered-кандидаты. Главная форма
модели edit-cost:
```text
vector: fixed + move_unit * sizeof(T) * (N - position)
tiered: fixed
+ move_unit * sizeof(T) * distance_inside_leaf
+ directory_unit * locality_multiplier * N / leaf
+ lookup(levels)
```
Первая переменная часть tiered растёт с leaf, вторая — с `N/leaf`. Поэтому
минимум сдвигается к большим блокам при росте N. Для близких последовательных
edit-позиций directory multiplier уменьшается: горячая область не создаёт
split во множестве разных leaf, и policy выбирает меньший блок.
Для каждого leaf вычисляется минимально достаточная глубина при текущих `N` и
fanout. Сравниваются три альтернативы: остаться, перейти в другое
представление, либо перестроить tiered с другой геометрией. Решение требует:
```text
(current_cost - candidate_cost) * forecast
> rebuild_cost * safety_factor
```
Для `vector→tiered`, `tiered→vector` и `tiered→tiered` используются разные
safety factor. Дополнительно действуют minimum residency, минимальное улучшение
shape, EWMA и два подтверждающих окна. После перехода статистическое окно
начинается заново. Все коэффициенты находятся в `AdaptationConfig` и могут
заменяться другой policy.
## Почему чтение по умолчанию не перестраивает storage
`ReadAdaptationMode::deferred` лишь записывает статистику. Pending-решение
выполняется на следующей мутации или при явном `adapt_now()`. Это сохраняет
важный контракт: обычное чтение не инвалидирует уже выданную ссылку.
`eager_nonconst` оставлен только как benchmark-эксперимент. Он может выполнить
переход вокруг non-const `operator[]`, добавляет проверку в горячий read path и
имеет более слабую семантику. Первые измерения не оправдали его как default.
Для read-only фазы, после которой мутаций нет, вызывающий код может поставить
явную maintenance point:
```cpp
values.adapt_now();
```
## Инвалидация
- const-чтение и сбор статистики в deferred mode ничего не инвалидируют;
- `set()` не меняет логические позиции, но ссылка на заменённое значение не
должна использоваться как ссылка на старый объект;
- любая структурная операция в auto mode может также выполнить rebuild, поэтому
инвалидирует все references, pointers и iterators;
- conversion, shape rebuild, `force_*`, `reserve`, `clear` и
`make_contiguous()` инвалидируют всё позиционное;
- stable ID переживает shifts, split и смену представления; он перестаёт быть
живым только после удаления элемента или `clear()`.
Debug-проверка iterator хранит structural generation и бросает `logic_error`
после инвалидирования.
## Hash index
`AdaptiveSequence<T, true>` использует open-addressed flat table
`value -> {head_id,count}`. Дубликаты связаны через компактные intrusive links в
ID metadata. `find_one` означает любой экземпляр. `find_all` возвращает
логически отсортированные позиции и потому может стоить O(k log k), тогда как
`find_all_ids` возвращает unordered stable IDs за O(k).