6.0 KiB
Архитектура
Представления
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
возвращает:
struct AdaptationDecision {
StorageMode target;
TieredConfig tiered_config;
double expected_saving;
};
Для каждого окна оцениваются vector и все tiered-кандидаты. Главная форма модели edit-cost:
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 с другой геометрией. Решение требует:
(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:
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).