New adaptation policy

This commit is contained in:
Efim Beshmenev
2026-08-13 23:42:26 +03:00
parent bbb43dbe57
commit 3770224eaf
16 changed files with 2533 additions and 489 deletions
+4
View File
@@ -89,4 +89,8 @@ if(UC_BUILD_BENCHMARKS)
add_executable(uc_bench src/benchmark.cpp) add_executable(uc_bench src/benchmark.cpp)
target_link_libraries(uc_bench PRIVATE universal_container) target_link_libraries(uc_bench PRIVATE universal_container)
set_property(TARGET uc_bench PROPERTY INTERPROCEDURAL_OPTIMIZATION FALSE) set_property(TARGET uc_bench PROPERTY INTERPROCEDURAL_OPTIMIZATION FALSE)
add_executable(uc_focused_bench src/focused_benchmark.cpp)
target_link_libraries(uc_focused_bench PRIVATE universal_container)
set_property(TARGET uc_focused_bench PROPERTY INTERPROCEDURAL_OPTIMIZATION FALSE)
endif() endif()
+45 -33
View File
@@ -1,31 +1,35 @@
# Universal Container # Universal Container
Экспериментальный C++20-контейнер индексируемой последовательности, который во Экспериментальный C++20-контейнер индексируемой последовательности с двумя
время работы выбирает представление и его геометрию: представлениями. Текущая default-policy привязывает все автоматические
перестройки к изменению логической вместимости:
```text ```text
contiguous vector <-> segmented tiered storage capacity boundary
|-- grow: capacity *= 2 when the current capacity is exhausted
`-- shrink: capacity /= 2 at 12.5% occupancy
| |
+-> leaf 64 / 128 / 256 / 512 / 1024 +-- small N: contiguous vector
+-> минимально достаточная глубина каталога `-- large N: tiered storage, leaf = ceil(sqrt(N))
``` ```
Это не контейнер с одним «лучшим» размером блока. `CostModelPolicy` собирает Между изменениями вместимости контейнер не меняет ни представление, ни размер
выборочную статистику чтений и полную статистику структурных изменений, leaf. На границе вместимости auto-mode заново выбирает `vector` или tiered,
оценивает стоимость кандидатов и выдаёт `AdaptationDecision`: целевой режим и поэтому обратный переход `tiered -> vector` также возможен. Focused cutoff
`TieredConfig`. Переход выполняется, только если прогнозируемая экономия на равен 4096 элементов; он выбран по baseline focused-run 7 x 100000 для
будущем горизонте превышает стоимость O(N)-перестройки с запасом. текущего `uint32_t` workload и остаётся калибруемой, а не универсальной
константой.
Проект исследовательский. Сохранённые измерения показывают, что оптимальный Поиск по значению не удалён: `AdaptiveSequence<T, true>` использует flat hash.
leaf действительно меняется с `N`, размером элемента и локальностью правок, но Число hash buckets рассчитывается из логической вместимости с запасом под
пока не доказывают превосходство adaptive-варианта во всех или в среднем по worst-case load не выше 70% и пересматривается только на той же границе
финальной holdout-матрице. Ограничения честно перечислены в вместимости. Это исключает независимый rehash посреди обычной серии операций.
Старые многомерные benchmark-матрицы и сделанные по ним выводы сохранены только
как **historical / retired**. Активная постановка и ограничения описаны в
[docs/benchmarking.md](docs/benchmarking.md) и
[docs/findings.md](docs/findings.md). [docs/findings.md](docs/findings.md).
`std::deque` и `std::list` присутствуют только как внешние benchmark-baselines.
Они не являются режимами контейнера: `AdaptiveSequence` переключается только
между contiguous vector и tiered storage.
## Быстрый старт ## Быстрый старт
Из обычной командной строки Windows: Из обычной командной строки Windows:
@@ -50,7 +54,8 @@ C++». `build.bat` сам находит комплектный CMake из Visua
- `uc_demo.exe` — минимальный пример; - `uc_demo.exe` — минимальный пример;
- `uc_tests.exe` — differential/property-тесты; - `uc_tests.exe` — differential/property-тесты;
- `uc_bench.exe` — benchmark runner. - `uc_focused_bench.exe` текущий focused benchmark runner;
- `uc_bench.exe` — historical / retired matrix runner.
Все Release-цели собираются с `/O2` и статическим MSVC runtime (`/MT`). Профиль Все Release-цели собираются с `/O2` и статическим MSVC runtime (`/MT`). Профиль
`scalar` — проверенный для MSVC 19.51 режим `project-novec`: внутренний ключ `scalar` — проверенный для MSVC 19.51 режим `project-novec`: внутренний ключ
@@ -61,16 +66,27 @@ AVX2-тестов и benchmark выполняется проверка CPU/OS;
безопасно пропускается. Подробности приведены в безопасно пропускается. Подробности приведены в
[docs/benchmarking.md](docs/benchmarking.md). [docs/benchmarking.md](docs/benchmarking.md).
Полный воспроизводимый запуск: Текущий воспроизводимый запуск:
```bat ```bat
benchmark.bat smoke benchmark.bat
benchmark.bat quick
benchmark.bat full
``` ```
Для multi-core сравнения `std::vector`, пяти fixed-tiered geometry и adaptive Он собирает и тестирует только профиль `baseline`, создаёт timestamp-папку и
вплоть до 100 миллионов `uint32_t`: один раз запускает `uc_focused_bench` с 100000 операций и 7 повторами. В
benchmark сравниваются три варианта одного типа
`AdaptiveSequence<uint32_t, true>`: forced vector, forced tiered и auto-mode;
flat hash включён у всех трёх. Mixed trace содержит 97% индексных чтений, 1%
hash-поисков `find_one` (50% hit / 50% miss), 1% вставок и 1% удалений. Runner
также печатает медианную пропускную способность каждого типа операций отдельно.
Raw CSV, metadata и медианная таблица `summary.md` сохраняются в
`results\benchmarks\<timestamp>-focused\`. Подтверждённая таблица публикуется
вместе с результатами запуска, а не вшивается в документацию до завершения
серии.
Следующие команды относятся к **historical / retired benchmark matrix** и не
являются текущим способом калибровки:
```bat ```bat
large_benchmark.bat smoke 12 large_benchmark.bat smoke 12
@@ -78,13 +94,9 @@ large_benchmark.bat full 12 3
large_benchmark.bat full 12 3 huge large_benchmark.bat full 12 3 huge
``` ```
Третий аргумент ограничивает число одновременно работающих N=100M-ячеек; Старые CSV оставлены для аудита истории, но напрямую не сравнимы с focused
значение 3 — эвристически более безопасный вариант для машины с 32 GiB RAM. постановкой: в ней другой workload и hash обязателен у каждого кандидата.
Четвёртый аргумент оставляет в матрице только N=100M. `out\` полностью игнорируется Git, результаты исследования — намеренно нет.
Каждый запуск создаёт отдельную timestamp-папку в `results\benchmarks\`, куда
пишутся raw CSV, сводки и описание окружения. `out\` полностью игнорируется
Git, результаты исследования — намеренно нет.
## API ## API
@@ -100,7 +112,7 @@ auto x = values[0];
values.force_vector_mode(); values.force_vector_mode();
values.force_tiered_mode({256, 64, 4}); values.force_tiered_mode({256, 64, 4});
values.enable_auto_mode(); values.enable_auto_mode();
values.adapt_now(); // явная безопасная maintenance point values.adapt_now(); // default-policy не меняет mode между resize
``` ```
Опциональный flat hash index со stable IDs: Опциональный flat hash index со stable IDs:
+12 -40
View File
@@ -2,56 +2,28 @@
setlocal EnableExtensions setlocal EnableExtensions
cd /d "%~dp0" cd /d "%~dp0"
set "UC_SCALE=%~1" if not "%~1"=="" (
if not defined UC_SCALE set "UC_SCALE=smoke" echo Usage: benchmark.bat
if /I not "%UC_SCALE%"=="smoke" if /I not "%UC_SCALE%"=="quick" if /I not "%UC_SCALE%"=="full" ( echo.
echo Usage: benchmark.bat [smoke^|quick^|full] echo The retired smoke/quick/full matrix is no longer run by this script.
exit /b 2 exit /b 2
) )
set "UC_PROFILES=scalar baseline" call build.bat baseline test
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "tools\test_avx2.ps1" if errorlevel 1 exit /b 1
if not errorlevel 1 set "UC_PROFILES=scalar baseline avx2"
for %%P in (%UC_PROFILES%) do ( for /f "delims=" %%I in ('powershell.exe -NoProfile -Command "Get-Date -Format yyyyMMdd-HHmmss"') do set "UC_RUN_ID=%%I-focused"
call build.bat %%P test
if errorlevel 1 exit /b 1
)
for /f "delims=" %%I in ('powershell.exe -NoProfile -Command "Get-Date -Format yyyyMMdd-HHmmss"') do set "UC_RUN_ID=%%I-%UC_SCALE%"
set "UC_RESULT_DIR=results\benchmarks\%UC_RUN_ID%" set "UC_RESULT_DIR=results\benchmarks\%UC_RUN_ID%"
mkdir "%UC_RESULT_DIR%" 2>nul mkdir "%UC_RESULT_DIR%" 2>nul
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "tools\write_benchmark_environment.ps1" -ResultDirectory "%UC_RESULT_DIR%" -RunId "%UC_RUN_ID%" -Scale "%UC_SCALE%" -Profiles "%UC_PROFILES%" powershell.exe -NoProfile -ExecutionPolicy Bypass -File "tools\write_benchmark_environment.ps1" -ResultDirectory "%UC_RESULT_DIR%" -RunId "%UC_RUN_ID%" -Scale "focused-100k-7x" -Profiles "baseline"
if errorlevel 1 exit /b 1 if errorlevel 1 exit /b 1
for %%P in (%UC_PROFILES%) do ( echo.
echo. echo === Focused resize benchmark: baseline /O2, 100000 operations, 7 repeats ===
echo === Tuning %%P /O2 profile === "out\bin\baseline\Release\uc_focused_bench.exe" --min-n 256 --max-n 65536 --operations 100000 --repeats 7 --output "%UC_RESULT_DIR%\baseline-focused.csv" > "%UC_RESULT_DIR%\summary.md"
"out\bin\%%P\Release\uc_bench.exe" --suite tune --scale "%UC_SCALE%" --fanout 64 --output "%UC_RESULT_DIR%\%%P-tune.csv" --summary "%UC_RESULT_DIR%\%%P-tune-summary.csv"
if errorlevel 1 exit /b 1
echo === Adaptive phase transitions for %%P ===
"out\bin\%%P\Release\uc_bench.exe" --suite adapt --scale "%UC_SCALE%" --leaf 512 --fanout 64 --levels 4 --output "%UC_RESULT_DIR%\%%P-adapt.csv" --summary "%UC_RESULT_DIR%\%%P-adapt-summary.csv"
if errorlevel 1 exit /b 1
echo === Comparing %%P /O2 profile ===
"out\bin\%%P\Release\uc_bench.exe" --suite compare --scale "%UC_SCALE%" --leaf 512 --fanout 64 --levels 4 --output "%UC_RESULT_DIR%\%%P-compare.csv" --summary "%UC_RESULT_DIR%\%%P-compare-summary.csv"
if errorlevel 1 exit /b 1
echo === Hash index and stable-ID overhead for %%P ===
"out\bin\%%P\Release\uc_bench.exe" --suite index --scale "%UC_SCALE%" --leaf 512 --fanout 64 --levels 4 --output "%UC_RESULT_DIR%\%%P-index.csv" --summary "%UC_RESULT_DIR%\%%P-index-summary.csv"
if errorlevel 1 exit /b 1
)
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "tools\analyze_results.ps1" -InputDirectory "%UC_RESULT_DIR%" -Pattern "*-compare.csv" -OutputFile "%UC_RESULT_DIR%\aggregate-compare.csv" -CellMedianFile "%UC_RESULT_DIR%\cell-medians-compare.csv"
if errorlevel 1 exit /b 1
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "tools\analyze_results.ps1" -InputDirectory "%UC_RESULT_DIR%" -Pattern "*-adapt.csv" -OutputFile "%UC_RESULT_DIR%\aggregate-adapt.csv" -CellMedianFile "%UC_RESULT_DIR%\cell-medians-adapt.csv"
if errorlevel 1 exit /b 1
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "tools\validate_adaptation.ps1" -CompareCellMedianFile "%UC_RESULT_DIR%\cell-medians-compare.csv" -AdaptCellMedianFile "%UC_RESULT_DIR%\cell-medians-adapt.csv"
if errorlevel 1 exit /b 1 if errorlevel 1 exit /b 1
type "%UC_RESULT_DIR%\summary.md"
echo. echo.
echo Benchmark run completed: %UC_RESULT_DIR% echo Benchmark run completed: %UC_RESULT_DIR%
+118 -82
View File
@@ -2,101 +2,137 @@
## Представления ## Представления
`AdaptiveSequence<T>` хранит `variant<vector<T>, TieredStorage<T>>`. Векторный `AdaptiveSequence<T>` хранит `variant<vector<T>, TieredStorage<T>>`.
режим предназначен для random/sequential read и append. Tiered-режим состоит из Contiguous vector выгоден для малых последовательностей и индексного чтения.
независимо выделенных циклических leaf-блоков (`RingBlock`) и многоуровневого Tiered storage состоит из независимо выделенных циклических leaf-блоков
каталога весов. Каталог строит только необходимое число уровней до заданного (`RingBlock`) и многоуровневого каталога весов.
максимума; поиск спускается по уровням, а отдельный Fenwick index восстанавливает
логическую позицию stable ID.
Текущий `TieredStorage` — сегментированный исследовательский вариант, а не У обоих backend есть единая **логическая вместимость** `capacity()`. Это не
полная реализация implicit tiered vector с offsets на каждом внутреннем узле. сумма физического slack в leaf-блоках, а управляющая граница, на которой
Offsets реально используются внутри leaf, но split/merge меняет массив разрешена автоматическая O(N)-перестройка.
дескрипторов leaf и перестраивает каталог. Это важное ограничение: некоторые
uniform-edit результаты отражают O(number_of_leaves) обслуживание split, а не
теоретическую границу полноценного tiered vector.
## Адаптивное решение Текущий `TieredStorage` остаётся сегментированным исследовательским вариантом, а
не полной реализацией implicit tiered vector с offsets/carry на каждом
внутреннем узле. Offsets используются внутри leaf, но split/merge может менять
массив leaf-дескрипторов и перестраивать каталог за O(number_of_leaves). Это
ограничение нужно учитывать при интерпретации результатов.
Storage и policy разделены. Контейнер передаёт `OperationSample`, policy ## Текущая default-policy: перестройка только с capacity
возвращает:
```cpp `ResizePolicy` не пытается распознать workload после каждой операции. В
struct AdaptationDecision { auto-mode представление и геометрия пересматриваются только вместе с изменением
StorageMode target; логической вместимости:
TieredConfig tiered_config;
double expected_saving;
};
```
Для каждого окна оцениваются vector и все tiered-кандидаты. Главная форма 1. Перед вставкой, которая не помещается, capacity удваивается до достаточного
модели edit-cost: значения. Для обычного роста это одна граница `C -> 2C`.
2. После удаления при `size <= capacity / 8`, то есть при заполнении не более
12.5%, capacity уменьшается на один шаг `C -> C/2` (но не ниже `size`).
3. Внутри той же перестройки выбираются mode, leaf geometry и размер hash
table. Между такими границами они остаются неизменными.
В auto-mode целевое представление определяется размером на момент перестройки:
```text ```text
vector: fixed + move_unit * sizeof(T) * (N - position) target_size < 4096 -> vector
target_size >= 4096 -> tiered
tiered: fixed
+ move_unit * sizeof(T) * distance_inside_leaf
+ directory_unit * locality_multiplier * N / leaf
+ lookup(levels)
``` ```
Первая переменная часть tiered растёт с leaf, вторая — с `N/leaf`. Поэтому `4096` — cutoff, подтверждённый текущим focused-run 7 x 100000 для
минимум сдвигается к большим блокам при росте N. Для близких последовательных `AdaptiveSequence<uint32_t, true>` и заданного mix; это не универсальный
edit-позиций directory multiplier уменьшается: горячая область не создаёт результат для всех типов и workload. Само пересечение `size=4096` немедленного перехода не
split во множестве разных leaf, и policy выбирает меньший блок. вызывает: mode меняется только на следующей capacity-boundary. Например, при
обычном последовательном росте контейнер остаётся vector при capacity 4096 и
переходит в tiered, когда следующая вставка меняет capacity на 8192. На shrink
boundary правило применяется заново, поэтому возможен обратный переход
`tiered -> vector`.
Для каждого leaf вычисляется минимально достаточная глубина при текущих `N` и Явные `force_vector_mode()`, `force_tiered_mode()` и `reserve()` остаются
fanout. Сравниваются три альтернативы: остаться, перейти в другое пользовательскими управляющими операциями и не являются автоматической
представление, либо перестроить tiered с другой геометрией. Решение требует: адаптацией.
## Геометрия tiered storage
При capacity-boundary размер малого массива вычисляется из фактического размера
последовательности на этой границе:
```text
leaf_capacity = max(4, ceil(sqrt(target_size)))
```
Значения fanout и допустимой глубины каталога берутся из `TieredConfig`.
`leaf_capacity` не подстраивается после каждой вставки или удаления: новое
`sqrt(N)` применяется только в общей перестройке. Поэтому rebuild одновременно
может выполнить `vector -> tiered`, `tiered -> vector` или
`tiered -> tiered` с новой геометрией.
## Flat hash index
`AdaptiveSequence<T, true>` использует open-addressed flat table
`value -> {head_id,count}`. Дубликаты связаны через компактные intrusive links в
ID metadata. `find_one` возвращает любой экземпляр, `find_all_ids` — unordered
stable IDs, а `find_all` сортирует логические позиции и может стоить
O(k log k).
Внутри цепочек используются 32-битные slot IDs. Освобождённые metadata slots
переиспользуются через free list, а публичный 64-битный stable ID кодирует
`generation + slot`. Поэтому сбалансированный insert/erase churn не наращивает
metadata без границ и старый ID не оживает после повторного использования slot.
Hash table следует той же capacity-policy, что и storage:
```text
required_buckets = ceil(logical_capacity / 0.70)
bucket_count = next_power_of_two(max(16, required_buckets))
```
Иными словами, таблица заранее рассчитана на худший случай «один уникальный
ключ на элемент» при load factor не выше 70%. При росте bucket reserve/rehash
выполняется перед перестройкой storage, при shrink — после неё. Целевое число
buckets пересчитывается только при изменении логической capacity; если оно не
изменилось, отдельного rehash нет. Обычные вставки и удаления между границами не
запускают независимое увеличение hash table.
Такой выбор сохраняет O(1) expected lookup и включает стоимость редкого rehash
в ту же измеряемую capacity-перестройку, где уже оплачивается перенос storage.
Flat hash остаётся compile-time optional: вариант `AdaptiveSequence<T, false>`
не несёт его память; тот же API поиска по значению работает линейным обходом.
## Инвалидация
- const-чтение и hash lookup ничего не инвалидируют;
- `set()` не меняет логические позиции, но ссылка на заменённое значение не
должна использоваться как ссылка на старый объект;
- вставка или удаление инвалидирует позиционные references, pointers и
iterators согласно структурному изменению; capacity-boundary дополнительно
может перестроить весь backend;
- conversion, geometry rebuild, `force_*`, `reserve`, `clear` и
`make_contiguous()` инвалидируют всё позиционное;
- stable ID переживает shifts, split и смену представления и перестаёт быть
живым только после удаления соответствующего элемента или `clear()`.
Stable ID имеет область действия конкретного состояния конкретного контейнера.
Copy/move assignment заменяет это состояние: ранее полученные ID обеих сторон
после assignment использовать нельзя (их числовое значение может совпасть с ID
элемента нового состояния). Move construction переносит состояние целиком и
сохраняет его ID в новом объекте.
Debug iterator хранит structural generation и бросает `logic_error` после
инвалидирования.
## Historical / retired: operation-window CostModelPolicy
До текущей постановки default-policy собирала `OperationSample`, оценивала
vector и набор фиксированных leaf 64/128/256/512/1024, применяла EWMA,
hysteresis и прогноз окупаемости O(N)-перехода:
```text ```text
(current_cost - candidate_cost) * forecast (current_cost - candidate_cost) * forecast
> rebuild_cost * safety_factor > rebuild_cost * safety_factor
``` ```
Для `vector→tiered`, `tiered→vector` и `tiered→tiered` используются разные Также исследовались deferred/eager read adaptation, minimum residency и
safety factor. Дополнительно действуют minimum residency, минимальное улучшение отдельные `vector -> tiered`, `tiered -> vector`, `tiered -> tiered` решения.
shape, EWMA и два подтверждающих окна. После перехода статистическое окно Эта схема и соответствующая benchmark-матрица **retired**: они сохранены для
начинается заново. Все коэффициенты находятся в `AdaptationConfig` и могут воспроизводимости старых CSV и для экспериментов с явно выбранной
заменяться другой policy. `CostModelPolicy`, но не описывают текущую default-policy и не используются для
нового cutoff.
## Почему чтение по умолчанию не перестраивает 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).
+113 -75
View File
@@ -1,99 +1,137 @@
# Сборка и benchmark-методика # Сборка и benchmark-методика
## Профили кода ## Активная focused-постановка
Все сравниваемые бинарники — Release x64 `/O2`, `/MT`, без IPO/LTO. Отличается Текущий benchmark отвечает на один практический вопрос: при каком `N` для
только ось SIMD: фиксированной малой доли правок становится выгодно переходить от contiguous
vector к tiered storage. Старую широкую матрицу типов, SIMD-профилей, leaf и
workload для этой калибровки не используют.
| Профиль | Ключи проекта | Назначение | Все три кандидата имеют один и тот же тип данных и один и тот же flat hash:
| Имя в CSV | Реализация | Режим |
|---|---|---| |---|---|---|
| `scalar` | `/O2 /d2Qvec- /Qvec-report:1 /Oi-` | MSVC-specific `project-novec` | | `forced_vector_hash` | `AdaptiveSequence<uint32_t, true>` | принудительно vector |
| `baseline` | `/O2`, без `/arch:AVX*` | обычный MSVC x64 | | `forced_tiered_hash` | `AdaptiveSequence<uint32_t, true>` | принудительно tiered |
| `avx2` | `/O2 /arch:AVX2` | разрешён AVX2 | | `adaptive_hash` | `AdaptiveSequence<uint32_t, true>` | текущая resize-bound auto-policy |
`/d2Qvec-` — внутренний, недокументированный ключ MSVC, а не переносимая Таким образом, поиск по значению не даёт одному кандидату скрытого преимущества:
гарантия. Командная строка проверена автоматическим аудитом для MSVC 19.51; стоимость хранения и обновления hash index присутствует у vector, tiered и
вывод `/Qvec-report` пока не разбирается как отдельное доказательство. `scalar` adaptive. Для forced tiered начальный leaf равен `ceil(sqrt(N))`. Auto-mode
не означает «ни одной SIMD-инструкции в процессе»: x64 ABI имеет SSE2-baseline, выбирает mode и leaf только при capacity-boundary.
а CRT `memmove` может runtime-dispatch-ить SIMD. Дополнительно задаётся
`_USE_STD_VECTOR_ALGORITHMS=0`, чтобы STL не включала свои явно
векторизованные алгоритмы. Это честнее называть `project-novec`.
## Матрица ## Трасса
Benchmark сравнивает: Mixed trace содержит ровно 100000 операций в default-запуске:
- reserved `std::vector`; | Операция | Доля | Default count | Детали |
- `std::deque`; |---|---:|---:|---|
- `std::list` с index semantics; | индексное чтение | 97% | 97000 | случайный существующий индекс |
- forced tiered для каждого leaf 64/128/256/512/1024; | hash find по значению | 1% | 1000 | `find_one`, 50% hit / 50% miss |
- adaptive deferred; | вставка | 1% | 1000 | случайная позиция |
- experimental adaptive eager-read. | удаление | 1% | 1000 | случайная существующая позиция |
`std::deque` и `std::list` — только внешние фиксированные baselines; Виды операций перемешиваются детерминированным seed. Вставки и удаления
`AdaptiveSequence` никогда в них не переключается. Для `std::list` runner сбалансированы, поэтому benchmark измеряет steady-size workload, но любая
эмулирует index semantics линейным проходом, поэтому доступ по индексу имеет реально достигнутая capacity-boundary и стоимость перехода остаются внутри
O(N). mixed timer. Построение исходного контейнера находится вне timer.
Типы: `uint32_t`, `uint64_t`, trivially-copyable 16/32/64-byte значения и Кроме общей `mixed ops/s`, runner измеряет отдельные homogeneous batches и
non-trivial movable string wrapper. Workloads включают random access, traversal, выводит `read ops/s`, `find ops/s`, `insert ops/s`, `erase ops/s`. Быстрые read
append, 99.99/99/80/50% reads, uniform/localized edits, bursty edits и отдельный и find batches содержат не менее 100000 вызовов для устойчивости таймера;
phase trace: фактические batch counts записываются в CSV. Поэтому отдельные колонки — это
пропускная способность соответствующего типа операции, а не время его 1%-доли
в mixed trace.
```text Один и тот же материализованный trace и seed применяются к трём кандидатам.
uniform edits -> maintenance Порядок кандидатов циклически меняется между paired repeats, чтобы фиксированная
localized edits -> maintenance первая или последняя позиция не принадлежала всегда одному представлению.
reads -> maintenance Checksum проверяет совпадение состояния и результатов операций.
## Capacity, leaf и hash во время измерения
Focused benchmark проверяет текущий контракт контейнера, а не внешнюю
эмуляцию:
- capacity увеличивается `x2`, когда очередная вставка не помещается;
- при `size <= capacity / 8` выполняется один shrink-шаг `capacity / 2`;
- mode и `leaf = ceil(sqrt(size))` пересчитываются только внутри этой
capacity-перестройки;
- focused cutoff auto-mode равен 4096;
- hash buckets рассчитываются из новой логической capacity с worst-case load
не выше 70% и не меняются независимо между capacity-boundaries.
Raw CSV сохраняет initial/final mode, leaf и logical capacity mixed-контейнера,
что позволяет увидеть включённый в mixed throughput переход `vector -> tiered`
или обратный переход. Для четырёх отдельных homogeneous batches поля
`read_mode`, `find_mode`, `insert_mode`, `erase_mode` и соответствующие
`*_leaf` отдельно фиксируют их фактический режим/геометрию; эти batches
стартуют с новых контейнеров и потому не обязаны повторять final mode/leaf
mixed-трассы.
## Воспроизводимый default-run
Из обычной командной строки Windows:
```bat
benchmark.bat
``` ```
Последний trace нужен именно для проверки смены leaf, а не только Скрипт выполняет ровно один focused-run:
`vector↔tiered`. Время rebuild остаётся внутри измеряемого end-to-end интервала.
Tuning перебирает leaf 64…1024 и maximum directory levels 2/3/4. Каталог сам 1. `build.bat baseline test` — Release x64 `/O2`, `/MT`, без `/arch:AVX*` и
останавливает построение на минимально достаточной глубине. без IPO/LTO, затем correctness-тесты.
2. Создаёт `results\benchmarks\<timestamp>-focused\` и записывает metadata и
source/binary manifests.
3. Запускает `uc_focused_bench` для степеней двойки `N=256...65536`, 100000
mixed operations и 7 paired repeats.
4. Печатает таблицу медиан operations/second, сохраняет её в `summary.md`, а
raw repeat data — в `baseline-focused.csv`.
Эквивалентный вызов runner после сборки:
```bat
out\bin\baseline\Release\uc_focused_bench.exe --min-n 256 --max-n 65536 --operations 100000 --repeats 7 --output results\benchmarks\focused.csv
```
`--operations` должен быть положительным числом, кратным 100. Доступны также
`--seed`, `--min-n` и `--max-n`; границы диапазона округляются до степеней
двойки.
Финальную таблицу следует публиковать в каталоге соответствующего запуска в
`results/benchmarks/`. Документация хранит только постановку и подтверждённые
выводы, а не копию ещё незавершённой серии.
## CSV ## CSV
Raw CSV содержит profile/type/workload/N/seed, initial и final tier config, Каждая raw-строка содержит:
total/ns-per-op, p50/p95/p99/max edit latency, checksum, allocated bytes,
storage switches, shape rebuilds и final mode. Checksum должен совпасть у всех
контейнеров одной трассы.
`tools/analyze_results.ps1` сначала берёт median повторов, затем сравнивает с - `profile`, `n`, `container`, `repeat`, `seed`;
лучшим фиксированным baseline в каждой ячейке. Итоговый geometric mean считается - initial/final mixed `mode`, `leaf`, `logical_capacity` и отдельные mode/leaf
иерархически по sizes → workloads → types → profiles, чтобы семейство с большим поля read/find/insert/erase batches;
числом строк не получило лишний вес. - mixed counts, включая hit/miss hash find;
- реальные размеры отдельных read/find/insert/erase batches;
- `mixed_ops_per_sec`, `read_ops_per_sec`, `find_ops_per_sec`,
`insert_ops_per_sec`, `erase_ops_per_sec`;
- оценку allocated bytes и checksum.
## Масштабы запуска Публикуемая таблица использует медиану семи повторов по каждой паре
`(N, container)`. Значения отдельных повторов не отбрасываются и остаются в
raw CSV.
```bat ## Historical / retired benchmark matrix
benchmark.bat smoke rem проверка всего конвейера
benchmark.bat quick rem рабочее исследование
benchmark.bat full rem финальные повторы, длительный запуск
```
Отдельная large-матрица с обязательным reserved vector, fixed leaf 64…1024 и Старый `uc_bench`, вызовы `benchmark.bat smoke|quick|full`, multi-profile
adaptive запускается внешним process pool. Второй аргумент — число workers: сравнение `scalar/baseline/avx2`, типы от `uint32_t` до blob/string wrapper,
fixed leaf 64...1024, `std::deque`, `std::list`, phase traces и
`large_benchmark.bat` до N=100M относятся к **historical / retired** методике.
```bat Их CSV сохранены для аудита истории проекта, но не участвуют в выборе текущего
large_benchmark.bat smoke 12 cutoff: набор кандидатов, mix операций и наличие hash index отличаются от
large_benchmark.bat full 12 3 focused постановки. Старые aggregate scores и speedup нельзя смешивать с новой
large_benchmark.bat full 12 3 huge таблицей operations/second.
```
Каждый worker закрепляется за отдельным logical CPU. Третий аргумент ограничивает Профили `scalar` и `avx2` по-прежнему можно собирать для отдельных инженерных
число одновременно работающих N=100M-ячеек и лишь снижает риск pagefile; это экспериментов. `scalar` использует внутренний MSVC `/d2Qvec-` и не является
эвристика, а не измеритель доступной памяти. Четвёртый аргумент `huge` оставляет универсальной гарантией отсутствия SIMD в CRT/ABI; это ещё одна причина не
только N=100M. Large raw, расширять текущий baseline-run до старой матрицы без отдельной задачи.
per-cell partial/log, план, environment и сводки сохраняются в отдельной папке
`results/benchmarks/`.
`large_benchmark.bat full 12 3 huge` воспроизводит только N=100M с удлинённым
512-edit horizon и сам проверяет поддержку AVX2. Прямой PowerShell runner такой
CPU-проверки не выполняет: при его ручном запуске `avx2` надо исключить на
неподдерживаемой машине.
Для публикации финального числа нужны одинаковый power plan, отсутствие тяжёлой
фоновой нагрузки, закрепление на физическом ядре, randomized paired order и
holdout workloads/seeds. Текущие quick-данные являются calibration set, а не
финальным доказательством.
+48 -5
View File
@@ -1,6 +1,49 @@
# Наблюдения и ограничения # Наблюдения и ограничения
## Что уже установлено ## Текущая focused-постановка
Активное исследование больше не использует старую benchmark-матрицу. Оно
сравнивает только три варианта `AdaptiveSequence<uint32_t, true>` с одинаковым
flat hash: forced vector, forced tiered и resize-bound adaptive. Mixed workload
фиксирован: 97% индексных чтений, 1% hash find (50% hit / 50% miss), 1% вставок
и 1% удалений.
Automatic policy меняет mode, `leaf = ceil(sqrt(N))` и размер hash table только
при изменении логической capacity. Рост capacity идёт `x2`; один shrink-шаг
`/2` выполняется при заполнении 12.5%. Поэтому цена перехода входит в измерение,
но ни storage, ни hash не перестраиваются после каждой отдельной операции.
## Final focused run и cutoff
Baseline focused-run с 7 повторами и 100000 mixed operations подтвердил
локальный crossover между двумя соседними размерами. При `N=2048` mixed median
forced vector равна **31.98M ops/s**, а forced tiered — **32.18M ops/s**:
преимущество tiered по медиане составляет лишь **0.618%**, при этом tiered
быстрее в **4 из 7**, а vector — в **3 из 7** paired repeats. Эта граница
считается неустойчивой и попадающей в шум измерения. При `N=4096` forced tiered
дал **32.53M ops/s**, forced vector — **17.20M ops/s**, а adaptive с включённым
в timer переходом `vector -> tiered`**32.64M ops/s**. Медиана tiered выше
vector в **1.8911 раза**, и tiered быстрее во всех **7 из 7** paired repeats.
Поэтому для текущего `uint32_t` workload принят **cutoff 4096** как первый
однозначный и устойчивый размер в sweep. Это подтверждённый результат
focused-серии, но всё ещё калибруемая граница, а не
универсальная константа для других типов и workload. Полная таблица медиан по
mixed/read/find/insert/erase и методика опубликованы в
[`20260813-focused-resize/README.md`](../results/benchmarks/20260813-focused-resize/README.md);
raw повторы находятся в
[`20260813-focused-resize/baseline.csv`](../results/benchmarks/20260813-focused-resize/baseline.csv).
В CSV 189 строк: у всех строк корректные счётчики операций, checksums совпадают
между тремя кандидатами для каждой пары `(N, repeat)`, а режимы и leaf отдельных
read/find/insert/erase batches записаны отдельными столбцами.
## Historical / retired benchmark evidence
Все результаты ниже относятся к прежним workload, кандидатам и policy. Они
сохранены для аудита истории, но **не участвуют** в выборе текущего cutoff и не
сравнимы напрямую с focused benchmark.
### Что было установлено старой методикой
На Ryzen 9 5900X / MSVC 19.51 quick-tuning дал разные оптимумы, поэтому На Ryzen 9 5900X / MSVC 19.51 quick-tuning дал разные оптимумы, поэтому
фиксированный `leaf=512` отвергнут как итоговая стратегия. Для baseline фиксированный `leaf=512` отвергнут как итоговая стратегия. Для baseline
@@ -26,7 +69,7 @@
доказательство, а сигнал, что старую таблицу leaf нужно заново проверить на доказательство, а сигнал, что старую таблицу leaf нужно заново проверить на
одинаковом quick holdout с тремя профилями. одинаковом quick holdout с тремя профилями.
## Hysteresis и итоговый quick-checkpoint ### Hysteresis и итоговый quick-checkpoint
После v13 выполнены три контрольные серии: После v13 выполнены три контрольные серии:
@@ -138,7 +181,7 @@ adaptive-deferred sequential `uint32` найден устойчивый AVX2 cod
1.85–1.98x относительно baseline во всех пяти повторах при верных checksums. 1.85–1.98x относительно baseline во всех пяти повторах при верных checksums.
Он требует отдельного анализа generated code, а не постфактум объяснения SIMD. Он требует отдельного анализа generated code, а не постфактум объяснения SIMD.
## Large-scale sweep до 100 миллионов элементов ### Large-scale sweep до 100 миллионов элементов
Серии [`large v1`](../results/benchmarks/20260811-large-full-12c-v1/README.md) Серии [`large v1`](../results/benchmarks/20260811-large-full-12c-v1/README.md)
и [`seed-jittered v2`](../results/benchmarks/20260811-large-n100m-512edit-12c-v2/README.md) и [`seed-jittered v2`](../results/benchmarks/20260811-large-n100m-512edit-12c-v2/README.md)
@@ -160,7 +203,7 @@ fixed oracle: до первого перехода он успевал выпо
делят L3/DRAM, а N>1M имеет один repeat на профиль. Поэтому SIMD-профили сохранены делят L3/DRAM, а N>1M имеет один repeat на профиль. Поэтому SIMD-профили сохранены
отдельно, но точный AVX2 speedup по этой серии не утверждается. отдельно, но точный AVX2 speedup по этой серии не утверждается.
## Чего пока нельзя утверждать ### Чего нельзя было утверждать по retired-сериям
- Не доказано, что adaptive implementation уже имеет geometric mean > 1 против - Не доказано, что adaptive implementation уже имеет geometric mean > 1 против
лучшего фиксированного контейнера на независимом full holdout. лучшего фиксированного контейнера на независимом full holdout.
@@ -183,7 +226,7 @@ fixed oracle: до первого перехода он успевал выпо
зафиксированной версии компилятора project-novec, но не публичный контракт зафиксированной версии компилятора project-novec, но не публичный контракт
MSVC и не доказательство отсутствия SIMD внутри CRT или x64 runtime. MSVC и не доказательство отсутствия SIMD внутри CRT или x64 runtime.
## Следующая исследовательская граница ### Бывшая исследовательская граница
Phase-aware hysteresis завершён и прошёл трёхпрофильный quick-validator. Phase-aware hysteresis завершён и прошёл трёхпрофильный quick-validator.
Следующий этап — независимый full holdout с несколькими seeds, affinity/pinning Следующий этап — независимый full holdout с несколькими seeds, affinity/pinning
@@ -151,6 +151,89 @@ struct AdaptationTelemetry {
std::size_t last_evidence_windows = 0; std::size_t last_evidence_windows = 0;
}; };
// The production policy is deliberately small. It does not try to infer a
// workload phase from every operation; AdaptiveSequence consults these limits
// only when its logical capacity grows or shrinks. The crossover value is a
// calibratable constant and is updated from the focused benchmark rather than
// from the retired benchmark matrix.
struct ResizePolicyConfig {
std::size_t minimum_tiered_size = 4 * 1024;
std::size_t shrink_denominator = 8; // shrink at 12.5% occupancy
};
class ResizePolicy {
public:
explicit ResizePolicy(ResizePolicyConfig config = {}, TieredConfig tiered = {}) noexcept
: config_(normalize(config)), active_tiered_(normalize_tiered(tiered)) {}
void set_tiered_config(TieredConfig tiered) noexcept {
active_tiered_ = normalize_tiered(tiered);
}
// Kept as no-op compatibility hooks so a custom CostModelPolicy can still
// be substituted for experiments without putting policy work on the
// default read/edit paths.
void observe(const OperationSample&) noexcept {}
[[nodiscard]] bool decision_ready() const noexcept { return false; }
[[nodiscard]] std::optional<AdaptationDecision>
recommended_decision(StorageMode, std::size_t, TieredConfig) noexcept {
return std::nullopt;
}
void on_transition(StorageMode from,
StorageMode to,
TieredConfig active_config) noexcept {
const auto normalized = normalize_tiered(active_config);
if (from == StorageMode::vector && to == StorageMode::tiered) {
++telemetry_.vector_to_tiered;
} else if (from == StorageMode::tiered && to == StorageMode::vector) {
++telemetry_.tiered_to_vector;
} else if (from == StorageMode::tiered && to == StorageMode::tiered) {
++telemetry_.tiered_rebuilds;
if (normalized.leaf_capacity == active_tiered_.leaf_capacity) {
++telemetry_.tiered_directory_rebuilds;
} else {
++telemetry_.tiered_leaf_rebuilds;
}
}
active_tiered_ = normalized;
}
void on_transition(StorageMode from, StorageMode to) noexcept {
on_transition(from, to, active_tiered_);
}
void reset() noexcept { telemetry_ = {}; }
[[nodiscard]] const ResizePolicyConfig& config() const noexcept { return config_; }
[[nodiscard]] const AdaptationTelemetry& telemetry() const noexcept {
return telemetry_;
}
private:
static ResizePolicyConfig normalize(ResizePolicyConfig config) noexcept {
config.minimum_tiered_size = std::max<std::size_t>(1,
config.minimum_tiered_size);
config.shrink_denominator = std::max<std::size_t>(2,
config.shrink_denominator);
return config;
}
static TieredConfig normalize_tiered(TieredConfig config) noexcept {
config.leaf_capacity = std::clamp<std::size_t>(
config.leaf_capacity, 4, 1U << 20U);
config.directory_fanout = std::clamp<std::size_t>(
config.directory_fanout, 2, 1U << 16U);
config.directory_levels = std::clamp<std::size_t>(
config.directory_levels, 1, 8);
return config;
}
ResizePolicyConfig config_{};
TieredConfig active_tiered_{};
AdaptationTelemetry telemetry_{};
};
class CostModelPolicy { class CostModelPolicy {
public: public:
static constexpr std::size_t candidate_count = 5; static constexpr std::size_t candidate_count = 5;
+441 -104
View File
@@ -10,6 +10,7 @@
#include <concepts> #include <concepts>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <cmath>
#include <functional> #include <functional>
#include <iterator> #include <iterator>
#include <limits> #include <limits>
@@ -25,7 +26,7 @@ namespace uc {
template <class T, template <class T,
bool HashIndexEnabled = false, bool HashIndexEnabled = false,
class AdaptationPolicy = CostModelPolicy, class AdaptationPolicy = ResizePolicy,
class Hash = std::hash<T>, class Hash = std::hash<T>,
class Equal = std::equal_to<T>> class Equal = std::equal_to<T>>
class AdaptiveSequence { class AdaptiveSequence {
@@ -54,8 +55,10 @@ private:
struct IdMetadata { struct IdMetadata {
internal_id previous = invalid_internal_id; internal_id previous = invalid_internal_id;
internal_id next = invalid_internal_id; internal_id next = invalid_internal_id;
internal_id free_next = invalid_internal_id;
std::uint32_t primary = 0; // vector index or stable leaf id std::uint32_t primary = 0; // vector index or stable leaf id
std::uint32_t secondary = 0; // local leaf index std::uint32_t secondary = 0; // local leaf index
std::uint32_t generation = 0;
std::uint8_t flags = 0; std::uint8_t flags = 0;
}; };
@@ -65,6 +68,7 @@ private:
struct IndexedState { struct IndexedState {
detail::FlatDuplicateIndex<T, Hash, Equal> values; detail::FlatDuplicateIndex<T, Hash, Equal> values;
std::vector<IdMetadata> ids; std::vector<IdMetadata> ids;
internal_id free_head = invalid_internal_id;
}; };
struct NoIndexState {}; struct NoIndexState {};
@@ -237,9 +241,53 @@ public:
} }
AdaptiveSequence(const AdaptiveSequence&) = default; AdaptiveSequence(const AdaptiveSequence&) = default;
AdaptiveSequence(AdaptiveSequence&&) noexcept = default;
AdaptiveSequence& operator=(const AdaptiveSequence&) = default; AdaptiveSequence(AdaptiveSequence&& other) noexcept
AdaptiveSequence& operator=(AdaptiveSequence&&) noexcept = default; : tiered_config_(std::move(other.tiered_config_)),
policy_(std::move(other.policy_)),
storage_(std::move(other.storage_)),
index_state_(std::move(other.index_state_)),
equal_(std::move(other.equal_)),
residency_(other.residency_),
read_adaptation_(other.read_adaptation_),
logical_capacity_(other.logical_capacity_),
read_sample_rate_(other.read_sample_rate_),
read_sample_countdown_(other.read_sample_countdown_),
edit_sample_rate_(other.edit_sample_rate_),
edit_sample_countdown_(other.edit_sample_countdown_),
generation_(other.generation_) {
other.reset_after_move();
}
AdaptiveSequence& operator=(const AdaptiveSequence& other) {
if (this != &other) {
AdaptiveSequence replacement(other);
*this = std::move(replacement);
}
return *this;
}
AdaptiveSequence& operator=(AdaptiveSequence&& other) noexcept {
if (this == &other) {
return *this;
}
const auto invalidated_generation = generation_ + 1;
tiered_config_ = std::move(other.tiered_config_);
policy_ = std::move(other.policy_);
storage_ = std::move(other.storage_);
index_state_ = std::move(other.index_state_);
equal_ = std::move(other.equal_);
residency_ = other.residency_;
read_adaptation_ = other.read_adaptation_;
logical_capacity_ = other.logical_capacity_;
read_sample_rate_ = other.read_sample_rate_;
read_sample_countdown_ = other.read_sample_countdown_;
edit_sample_rate_ = other.edit_sample_rate_;
edit_sample_countdown_ = other.edit_sample_countdown_;
generation_ = invalidated_generation;
other.reset_after_move();
return *this;
}
~AdaptiveSequence() = default; ~AdaptiveSequence() = default;
[[nodiscard]] size_type size() const noexcept { [[nodiscard]] size_type size() const noexcept {
@@ -249,6 +297,10 @@ public:
} }
[[nodiscard]] bool empty() const noexcept { return size() == 0; } [[nodiscard]] bool empty() const noexcept { return size() == 0; }
// Logical capacity is shared by both backends. It is the only trigger for
// automatic representation/geometry changes; TieredStorage's internal
// leaf slack is intentionally not exposed as container capacity.
[[nodiscard]] size_type capacity() const noexcept { return logical_capacity_; }
[[nodiscard]] StorageMode mode() const noexcept { [[nodiscard]] StorageMode mode() const noexcept {
return std::holds_alternative<vector_storage>(storage_) return std::holds_alternative<vector_storage>(storage_)
? StorageMode::vector ? StorageMode::vector
@@ -258,13 +310,7 @@ public:
[[nodiscard]] const TieredConfig& tiered_config() const noexcept { return tiered_config_; } [[nodiscard]] const TieredConfig& tiered_config() const noexcept { return tiered_config_; }
reference operator[](size_type index) { reference operator[](size_type index) {
if (read_adaptation_ == ReadAdaptationMode::eager_nonconst) {
apply_pending_adaptation();
}
observe_random_read(); observe_random_read();
if (read_adaptation_ == ReadAdaptationMode::eager_nonconst) {
apply_pending_adaptation();
}
if constexpr (HashIndexEnabled) { if constexpr (HashIndexEnabled) {
return reference_proxy(*this, index); return reference_proxy(*this, index);
} else { } else {
@@ -295,7 +341,6 @@ public:
void erase(size_type index) { void erase(size_type index) {
check_index(index); check_index(index);
apply_pending_adaptation();
const auto old_size = size(); const auto old_size = size();
const auto id = record_id(record_at_unchecked(index)); const auto id = record_id(record_at_unchecked(index));
if constexpr (HashIndexEnabled) { if constexpr (HashIndexEnabled) {
@@ -311,13 +356,11 @@ public:
(void)std::get<tiered_storage>(storage_).erase(index, relocate); (void)std::get<tiered_storage>(storage_).erase(index, relocate);
} }
if constexpr (HashIndexEnabled) { if constexpr (HashIndexEnabled) {
auto& metadata = index_state_.ids[static_cast<size_type>(id)]; release_id_slot(id);
metadata.flags &= static_cast<std::uint8_t>(~alive_flag);
metadata.previous = invalid_internal_id;
metadata.next = invalid_internal_id;
} }
++generation_; ++generation_;
observe_structural_edit(OperationKind::erase, old_size, index); observe_structural_edit(OperationKind::erase, old_size, index);
shrink_after_erase();
} }
void set(size_type index, const T& value) { set_impl(index, T(value)); } void set(size_type index, const T& value) { set_impl(index, T(value)); }
@@ -325,14 +368,11 @@ public:
void clear() { void clear() {
if constexpr (HashIndexEnabled) { if constexpr (HashIndexEnabled) {
for (auto& metadata : index_state_.ids) {
metadata.flags &= static_cast<std::uint8_t>(~alive_flag);
metadata.previous = invalid_internal_id;
metadata.next = invalid_internal_id;
}
index_state_.values.clear(); index_state_.values.clear();
rebuild_free_id_list();
} }
storage_.template emplace<vector_storage>(); storage_.template emplace<vector_storage>();
logical_capacity_ = 0;
residency_ = ResidencyMode::automatic; residency_ = ResidencyMode::automatic;
policy_.reset(); policy_.reset();
read_sample_countdown_ = read_sample_rate_; read_sample_countdown_ = read_sample_rate_;
@@ -342,9 +382,12 @@ public:
void reserve(size_type capacity) { void reserve(size_type capacity) {
force_vector_mode(); force_vector_mode();
std::get<vector_storage>(storage_).reserve(capacity); const auto requested = std::max(capacity, size());
std::get<vector_storage>(storage_).reserve(requested);
logical_capacity_ = std::max(logical_capacity_, requested);
if constexpr (HashIndexEnabled) { if constexpr (HashIndexEnabled) {
const auto additional = capacity > size() ? capacity - size() : 0; index_state_.values.reserve_for_elements(logical_capacity_);
const auto additional = requested > size() ? requested - size() : 0;
index_state_.ids.reserve(index_state_.ids.size() + additional); index_state_.ids.reserve(index_state_.ids.size() + additional);
} }
++generation_; ++generation_;
@@ -364,15 +407,24 @@ public:
if (!entry || entry->head == invalid_internal_id) { if (!entry || entry->head == invalid_internal_id) {
return std::nullopt; return std::nullopt;
} }
return resolve_id(entry->head); return resolve_internal_id(entry->head);
} else { } else {
for (size_type i = 0; i < size(); ++i) { if (mode() == StorageMode::vector) {
if (equal_(value_at_unchecked(i), value)) { const auto& values = std::get<vector_storage>(storage_);
for (size_type i = 0; i < values.size(); ++i) {
if (equal_(record_value(values[i]), value)) {
return i; return i;
} }
} }
return std::nullopt; return std::nullopt;
} }
const auto found = std::get<tiered_storage>(storage_).find_if(
[&](const record_type& record) {
return equal_(record_value(record), value);
});
return found == tiered_storage::npos
? std::nullopt : std::optional<size_type>(found);
}
} }
[[nodiscard]] std::vector<size_type> find_all(const T& value) const { [[nodiscard]] std::vector<size_type> find_all(const T& value) const {
@@ -385,16 +437,27 @@ public:
result.reserve(entry->count); result.reserve(entry->count);
auto id = entry->head; auto id = entry->head;
while (id != invalid_internal_id) { while (id != invalid_internal_id) {
result.push_back(resolve_id(id)); result.push_back(resolve_internal_id(id));
id = index_state_.ids[static_cast<size_type>(id)].next; id = index_state_.ids[static_cast<size_type>(id)].next;
} }
std::sort(result.begin(), result.end()); std::sort(result.begin(), result.end());
} else { } else {
for (size_type i = 0; i < size(); ++i) { if (mode() == StorageMode::vector) {
if (equal_(value_at_unchecked(i), value)) { const auto& values = std::get<vector_storage>(storage_);
for (size_type i = 0; i < values.size(); ++i) {
if (equal_(record_value(values[i]), value)) {
result.push_back(i); result.push_back(i);
} }
} }
} else {
std::get<tiered_storage>(storage_).for_each_match(
[&](const record_type& record) {
return equal_(record_value(record), value);
},
[&](size_type index, const record_type&) {
result.push_back(index);
});
}
} }
return result; return result;
} }
@@ -411,7 +474,7 @@ public:
result.reserve(entry->count); result.reserve(entry->count);
auto id = entry->head; auto id = entry->head;
while (id != invalid_internal_id) { while (id != invalid_internal_id) {
result.push_back(static_cast<stable_id>(id)); result.push_back(stable_id_for_slot(id));
id = index_state_.ids[static_cast<size_type>(id)].next; id = index_state_.ids[static_cast<size_type>(id)].next;
} }
return result; return result;
@@ -432,11 +495,11 @@ public:
if (!entry) { if (!entry) {
return 0; return 0;
} }
std::vector<internal_id> ids; std::vector<stable_id> ids;
ids.reserve(entry->count); ids.reserve(entry->count);
auto id = entry->head; auto id = entry->head;
while (id != invalid_internal_id) { while (id != invalid_internal_id) {
ids.push_back(id); ids.push_back(stable_id_for_slot(id));
id = index_state_.ids[static_cast<size_type>(id)].next; id = index_state_.ids[static_cast<size_type>(id)].next;
} }
for (const auto current : ids) { for (const auto current : ids) {
@@ -460,14 +523,22 @@ public:
[[nodiscard]] stable_id id_at(size_type index) const [[nodiscard]] stable_id id_at(size_type index) const
requires(HashIndexEnabled) { requires(HashIndexEnabled) {
check_index(index); check_index(index);
return static_cast<stable_id>(record_at_unchecked(index).id); return stable_id_for_slot(record_at_unchecked(index).id);
} }
[[nodiscard]] bool id_alive(stable_id id) const noexcept [[nodiscard]] bool id_alive(stable_id id) const noexcept
requires(HashIndexEnabled) { requires(HashIndexEnabled) {
return id <= static_cast<stable_id>(std::numeric_limits<internal_id>::max()) if (id == invalid_id) {
&& id < index_state_.ids.size() return false;
&& (index_state_.ids[static_cast<size_type>(id)].flags & alive_flag) != 0; }
const auto slot = slot_from_stable_id(id);
if (slot == invalid_internal_id
|| static_cast<size_type>(slot) >= index_state_.ids.size()) {
return false;
}
const auto& metadata = index_state_.ids[static_cast<size_type>(slot)];
return metadata.generation == generation_from_stable_id(id)
&& (metadata.flags & alive_flag) != 0;
} }
void erase_by_id(stable_id id) void erase_by_id(stable_id id)
@@ -532,12 +603,11 @@ public:
return read_adaptation_; return read_adaptation_;
} }
bool adapt_now() { // Automatic rebuilds are capacity-boundary operations. This compatibility
if (residency_ != ResidencyMode::automatic) { // hook therefore never changes the representation between two resize
return false; // events; explicit force_* calls remain available for callers that need an
} // immediate manual conversion.
return apply_pending_adaptation(); bool adapt_now() noexcept { return false; }
}
[[nodiscard]] T* data() noexcept [[nodiscard]] T* data() noexcept
requires(!HashIndexEnabled) { requires(!HashIndexEnabled) {
@@ -625,10 +695,29 @@ public:
return bytes; return bytes;
} }
[[nodiscard]] std::size_t hash_bucket_count() const noexcept
requires(HashIndexEnabled) {
return index_state_.values.bucket_count();
}
[[nodiscard]] AdaptationPolicy& policy() noexcept { return policy_; } [[nodiscard]] AdaptationPolicy& policy() noexcept { return policy_; }
[[nodiscard]] const AdaptationPolicy& policy() const noexcept { return policy_; } [[nodiscard]] const AdaptationPolicy& policy() const noexcept { return policy_; }
private: private:
void reset_after_move() noexcept {
storage_.template emplace<vector_storage>();
if constexpr (HashIndexEnabled) {
index_state_.values.reset_after_move();
index_state_.ids.clear();
index_state_.free_head = invalid_internal_id;
}
residency_ = ResidencyMode::automatic;
logical_capacity_ = 0;
read_sample_countdown_ = read_sample_rate_;
edit_sample_countdown_ = edit_sample_rate_;
++generation_;
}
static void set_location(IdMetadata& metadata, static void set_location(IdMetadata& metadata,
bool tiered, bool tiered,
std::uint32_t primary, std::uint32_t primary,
@@ -667,14 +756,88 @@ private:
} }
} }
record_type make_record(T value) { static constexpr unsigned stable_id_slot_bits =
if constexpr (HashIndexEnabled) { std::numeric_limits<internal_id>::digits;
static_assert(stable_id_slot_bits == 32);
[[nodiscard]] static internal_id slot_from_stable_id(stable_id id) noexcept {
return static_cast<internal_id>(id);
}
[[nodiscard]] static std::uint32_t generation_from_stable_id(
stable_id id) noexcept {
return static_cast<std::uint32_t>(id >> stable_id_slot_bits);
}
[[nodiscard]] stable_id stable_id_for_slot(internal_id slot) const noexcept
requires(HashIndexEnabled) {
const auto generation =
index_state_.ids[static_cast<size_type>(slot)].generation;
return (static_cast<stable_id>(generation) << stable_id_slot_bits)
| static_cast<stable_id>(slot);
}
[[nodiscard]] internal_id acquire_id_slot()
requires(HashIndexEnabled) {
if (index_state_.free_head != invalid_internal_id) {
const auto id = index_state_.free_head;
auto& metadata = index_state_.ids[static_cast<size_type>(id)];
index_state_.free_head = metadata.free_next;
assert(metadata.generation
< std::numeric_limits<std::uint32_t>::max());
const auto next_generation = metadata.generation + 1;
metadata = {};
metadata.generation = next_generation;
return id;
}
if (index_state_.ids.size() >= static_cast<size_type>(invalid_internal_id)) { if (index_state_.ids.size() >= static_cast<size_type>(invalid_internal_id)) {
throw std::length_error("stable id space exhausted"); throw std::length_error("stable id slot space exhausted");
} }
const auto id = static_cast<internal_id>(index_state_.ids.size()); const auto id = static_cast<internal_id>(index_state_.ids.size());
index_state_.ids.emplace_back(); index_state_.ids.emplace_back();
return id;
}
void release_id_slot(internal_id id) noexcept
requires(HashIndexEnabled) {
assert(id != invalid_internal_id);
assert(static_cast<size_type>(id) < index_state_.ids.size());
auto& metadata = index_state_.ids[static_cast<size_type>(id)];
const auto generation = metadata.generation;
metadata = {};
metadata.generation = generation;
if (generation != std::numeric_limits<std::uint32_t>::max()) {
metadata.free_next = index_state_.free_head;
index_state_.free_head = id;
}
}
void rebuild_free_id_list() noexcept
requires(HashIndexEnabled) {
index_state_.free_head = invalid_internal_id;
for (size_type index = index_state_.ids.size(); index != 0; --index) {
const auto id = static_cast<internal_id>(index - 1);
auto& metadata = index_state_.ids[index - 1];
const auto generation = metadata.generation;
metadata = {};
metadata.generation = generation;
if (generation != std::numeric_limits<std::uint32_t>::max()) {
metadata.free_next = index_state_.free_head;
index_state_.free_head = id;
}
}
}
record_type make_record(T value) {
if constexpr (HashIndexEnabled) {
const auto id = acquire_id_slot();
try {
return IndexedRecord{std::move(value), id}; return IndexedRecord{std::move(value), id};
} catch (...) {
release_id_slot(id);
throw;
}
} else { } else {
return value; return value;
} }
@@ -707,22 +870,39 @@ private:
} }
void push_back_impl(T value) { void push_back_impl(T value) {
apply_pending_adaptation();
const auto old_size = size(); const auto old_size = size();
grow_before_insert(old_size + 1);
auto record = make_record(std::move(value)); auto record = make_record(std::move(value));
const auto id = record_id(record); const auto id = record_id(record);
auto relocate = relocation_callback(); auto relocate = relocation_callback();
if constexpr (HashIndexEnabled) {
try {
// Keep an unmoved lookup key so a failed storage insertion can
// roll the duplicate chain back before the slot is recycled.
T rollback_key(record.value);
link_value(record.value, id);
try {
if (mode() == StorageMode::vector) { if (mode() == StorageMode::vector) {
auto& values = std::get<vector_storage>(storage_); auto& values = std::get<vector_storage>(storage_);
values.push_back(std::move(record)); values.push_back(std::move(record));
refresh_vector_locations(old_size); refresh_vector_locations(old_size);
} else { } else {
std::get<tiered_storage>(storage_).push_back(std::move(record), relocate); std::get<tiered_storage>(storage_).push_back(
std::move(record), relocate);
} }
if constexpr (HashIndexEnabled) { } catch (...) {
auto& metadata = index_state_.ids[static_cast<size_type>(id)]; unlink_value(rollback_key, id);
metadata.flags |= alive_flag; throw;
link_value(value_at_unchecked(old_size), id); }
index_state_.ids[static_cast<size_type>(id)].flags |= alive_flag;
} catch (...) {
release_id_slot(id);
throw;
}
} else if (mode() == StorageMode::vector) {
std::get<vector_storage>(storage_).push_back(std::move(record));
} else {
std::get<tiered_storage>(storage_).push_back(std::move(record), relocate);
} }
++generation_; ++generation_;
policy_.observe({OperationKind::append, old_size, old_size, 1, sizeof(T)}); policy_.observe({OperationKind::append, old_size, old_size, 1, sizeof(T)});
@@ -732,22 +912,40 @@ private:
if (index > size()) { if (index > size()) {
throw std::out_of_range("AdaptiveSequence insertion index out of range"); throw std::out_of_range("AdaptiveSequence insertion index out of range");
} }
apply_pending_adaptation();
const auto old_size = size(); const auto old_size = size();
grow_before_insert(old_size + 1);
auto record = make_record(std::move(value)); auto record = make_record(std::move(value));
const auto id = record_id(record); const auto id = record_id(record);
auto relocate = relocation_callback(); auto relocate = relocation_callback();
if constexpr (HashIndexEnabled) {
try {
T rollback_key(record.value);
link_value(record.value, id);
try {
if (mode() == StorageMode::vector) { if (mode() == StorageMode::vector) {
auto& values = std::get<vector_storage>(storage_); auto& values = std::get<vector_storage>(storage_);
values.insert(values.begin() + static_cast<difference_type>(index), std::move(record)); values.insert(values.begin() + static_cast<difference_type>(index),
std::move(record));
refresh_vector_locations(index); refresh_vector_locations(index);
} else { } else {
std::get<tiered_storage>(storage_).insert(index, std::move(record), relocate); std::get<tiered_storage>(storage_).insert(
index, std::move(record), relocate);
} }
if constexpr (HashIndexEnabled) { } catch (...) {
auto& metadata = index_state_.ids[static_cast<size_type>(id)]; unlink_value(rollback_key, id);
metadata.flags |= alive_flag; throw;
link_value(value_at_unchecked(index), id); }
index_state_.ids[static_cast<size_type>(id)].flags |= alive_flag;
} catch (...) {
release_id_slot(id);
throw;
}
} else if (mode() == StorageMode::vector) {
auto& values = std::get<vector_storage>(storage_);
values.insert(values.begin() + static_cast<difference_type>(index),
std::move(record));
} else {
std::get<tiered_storage>(storage_).insert(index, std::move(record), relocate);
} }
++generation_; ++generation_;
observe_structural_edit(OperationKind::insert, old_size, index); observe_structural_edit(OperationKind::insert, old_size, index);
@@ -837,13 +1035,28 @@ private:
[[nodiscard]] size_type resolve_id(stable_id id) const [[nodiscard]] size_type resolve_id(stable_id id) const
requires(HashIndexEnabled) { requires(HashIndexEnabled) {
if (id >= index_state_.ids.size()) { if (id == invalid_id) {
throw std::out_of_range("unknown stable id"); throw std::out_of_range("unknown stable id");
} }
const auto& metadata = index_state_.ids[static_cast<size_type>(id)]; const auto slot = slot_from_stable_id(id);
if ((metadata.flags & alive_flag) == 0) { if (slot == invalid_internal_id
|| static_cast<size_type>(slot) >= index_state_.ids.size()) {
throw std::out_of_range("unknown stable id");
}
const auto& metadata = index_state_.ids[static_cast<size_type>(slot)];
if (metadata.generation != generation_from_stable_id(id)
|| (metadata.flags & alive_flag) == 0) {
throw std::out_of_range("stable id no longer refers to an element"); throw std::out_of_range("stable id no longer refers to an element");
} }
return resolve_internal_id(slot);
}
[[nodiscard]] size_type resolve_internal_id(internal_id id) const noexcept
requires(HashIndexEnabled) {
assert(id != invalid_internal_id);
assert(static_cast<size_type>(id) < index_state_.ids.size());
const auto& metadata = index_state_.ids[static_cast<size_type>(id)];
assert((metadata.flags & alive_flag) != 0);
if ((metadata.flags & tiered_flag) == 0) { if ((metadata.flags & tiered_flag) == 0) {
return metadata.primary; return metadata.primary;
} }
@@ -852,51 +1065,171 @@ private:
metadata.secondary); metadata.secondary);
} }
bool apply_pending_adaptation() { [[nodiscard]] static size_type ceil_sqrt(size_type value) noexcept {
if (residency_ != ResidencyMode::automatic || !policy_.decision_ready()) { if (value <= 1) {
return false; return value;
}
auto root = static_cast<size_type>(std::sqrt(static_cast<long double>(value)));
while (root > value / root) {
--root;
}
while (root < value / root
|| (root <= value / root && root * root < value)) {
++root;
}
return root;
} }
if constexpr (requires(AdaptationPolicy& p, StorageMode current, [[nodiscard]] size_type minimum_tiered_size() const noexcept {
size_type count, TieredConfig config) { if constexpr (requires(const AdaptationPolicy& p) {
p.recommended_decision(current, count, config); p.config().minimum_tiered_size;
}) { }) {
const auto recommendation = return std::max<size_type>(1, policy_.config().minimum_tiered_size);
policy_.recommended_decision(mode(), size(), tiered_config_);
if (!recommendation) {
return false;
}
if (recommendation->target == mode()) {
if (mode() == StorageMode::tiered
&& recommendation->tiered_config != tiered_config_) {
reconfigure_tiered(recommendation->tiered_config);
return true;
}
return false;
}
if (recommendation->target == StorageMode::tiered) {
const auto previous_config = tiered_config_;
tiered_config_ = recommendation->tiered_config;
try {
convert_to(recommendation->target);
} catch (...) {
// The recommended shape is policy state, not committed
// container state. A failed allocation/copy must not make
// tiered_config() describe storage that was never built.
tiered_config_ = previous_config;
throw;
}
return true;
}
convert_to(recommendation->target);
return true;
} else { } else {
const auto recommendation = policy_.recommended_mode(mode(), size()); return 4 * 1024;
if (!recommendation || *recommendation == mode()) { }
return false; }
[[nodiscard]] size_type shrink_denominator() const noexcept {
if constexpr (requires(const AdaptationPolicy& p) {
p.config().shrink_denominator;
}) {
return std::max<size_type>(2, policy_.config().shrink_denominator);
} else {
return 8;
}
}
[[nodiscard]] StorageMode automatic_mode_for(size_type element_count) const noexcept {
return element_count >= minimum_tiered_size()
? StorageMode::tiered : StorageMode::vector;
}
[[nodiscard]] TieredConfig geometry_for(size_type element_count) const noexcept {
auto result = tiered_config_;
result.leaf_capacity = std::max<size_type>(4, ceil_sqrt(element_count));
return result;
}
void grow_before_insert(size_type required) {
if (required <= logical_capacity_) {
return;
}
size_type next = logical_capacity_ == 0 ? 1 : logical_capacity_;
while (next < required) {
if (next > std::numeric_limits<size_type>::max() / 2) {
next = required;
break;
}
next *= 2;
}
rebuild_for_capacity(next, required);
}
void shrink_after_erase() noexcept {
if (logical_capacity_ <= 1
|| size() > logical_capacity_ / shrink_denominator()) {
return;
}
const auto next = std::max(size(), logical_capacity_ / 2);
// The erase has already committed. Capacity shrink is an automatic
// optimization and must not turn that successful erase into a thrown
// operation. Copyable records keep their source intact on failure;
// nothrow-movable records use preallocated rebuild paths below.
if constexpr (std::is_copy_constructible_v<record_type>
|| std::is_nothrow_move_constructible_v<record_type>) {
try {
rebuild_for_capacity(next, size());
} catch (...) {
// Keep the current (or already committed) capacity/layout and
// retry at a later resize boundary.
}
}
}
void rebuild_for_capacity(size_type target_capacity, size_type target_size) {
target_capacity = std::max(target_capacity, target_size);
const bool growing = target_capacity >= logical_capacity_;
if constexpr (HashIndexEnabled) {
if (growing) {
index_state_.values.reserve_for_elements(target_capacity);
index_state_.ids.reserve(
std::max(index_state_.ids.size(), target_capacity));
}
}
auto target_mode = mode();
if (residency_ == ResidencyMode::forced_vector) {
target_mode = StorageMode::vector;
} else if (residency_ == ResidencyMode::forced_tiered) {
target_mode = StorageMode::tiered;
} else {
target_mode = automatic_mode_for(target_size);
}
if (target_mode == StorageMode::vector) {
vector_storage rebuilt;
rebuilt.reserve(target_capacity);
if (mode() == StorageMode::vector) {
auto& source = std::get<vector_storage>(storage_);
if constexpr (std::is_copy_constructible_v<record_type>) {
rebuilt.insert(rebuilt.end(), source.begin(), source.end());
} else {
rebuilt.insert(rebuilt.end(),
std::make_move_iterator(source.begin()),
std::make_move_iterator(source.end()));
}
} else if constexpr (std::is_copy_constructible_v<record_type>) {
const auto& source = std::get<tiered_storage>(storage_);
source.for_each([&](const record_type& record) {
rebuilt.push_back(record);
});
} else {
auto& source = std::get<tiered_storage>(storage_);
source.for_each([&](record_type& record) {
rebuilt.push_back(std::move(record));
});
}
const auto source_mode = mode();
storage_.template emplace<vector_storage>(std::move(rebuilt));
logical_capacity_ = target_capacity;
refresh_vector_locations(0);
++generation_;
if (source_mode != StorageMode::vector) {
notify_policy_transition(source_mode, StorageMode::vector);
}
} else {
const auto target_config = geometry_for(target_size);
tiered_storage rebuilt;
if (mode() == StorageMode::vector) {
if constexpr (std::is_copy_constructible_v<record_type>) {
rebuilt = tiered_storage::from_vector_copy(
std::get<vector_storage>(storage_), target_config);
} else {
rebuilt = tiered_storage::from_vector(
std::move(std::get<vector_storage>(storage_)), target_config);
}
} else if constexpr (std::is_copy_constructible_v<record_type>) {
rebuilt = tiered_storage::reconfigured_copy(
std::get<tiered_storage>(storage_), target_config);
} else {
rebuilt = tiered_storage::reconfigured_move(
std::get<tiered_storage>(storage_), target_config);
}
const auto source_mode = mode();
tiered_config_ = rebuilt.config();
storage_.template emplace<tiered_storage>(std::move(rebuilt));
logical_capacity_ = target_capacity;
refresh_tiered_locations();
++generation_;
notify_policy_transition(source_mode, StorageMode::tiered);
}
if constexpr (HashIndexEnabled) {
if (!growing) {
index_state_.values.reserve_for_elements(target_capacity);
} }
convert_to(*recommendation);
return true;
} }
} }
@@ -911,8 +1244,8 @@ private:
tiered = tiered_storage::from_vector_copy( tiered = tiered_storage::from_vector_copy(
std::get<vector_storage>(storage_), tiered_config_); std::get<vector_storage>(storage_), tiered_config_);
} else { } else {
auto values = std::move(std::get<vector_storage>(storage_)); tiered = tiered_storage::from_vector(
tiered = tiered_storage::from_vector(std::move(values), tiered_config_); std::move(std::get<vector_storage>(storage_)), tiered_config_);
} }
tiered_config_ = tiered.config(); tiered_config_ = tiered.config();
storage_.template emplace<tiered_storage>(std::move(tiered)); storage_.template emplace<tiered_storage>(std::move(tiered));
@@ -924,6 +1257,9 @@ private:
} else { } else {
values = std::get<tiered_storage>(storage_).to_vector_move(); values = std::get<tiered_storage>(storage_).to_vector_move();
} }
if (logical_capacity_ > values.capacity()) {
values.reserve(logical_capacity_);
}
storage_.template emplace<vector_storage>(std::move(values)); storage_.template emplace<vector_storage>(std::move(values));
refresh_vector_locations(0); refresh_vector_locations(0);
} }
@@ -955,8 +1291,8 @@ private:
rebuilt = tiered_storage::reconfigured_copy( rebuilt = tiered_storage::reconfigured_copy(
std::get<tiered_storage>(storage_), target_config); std::get<tiered_storage>(storage_), target_config);
} else { } else {
auto values = std::get<tiered_storage>(storage_).to_vector_move(); rebuilt = tiered_storage::reconfigured_move(
rebuilt = tiered_storage::from_vector(std::move(values), target_config); std::get<tiered_storage>(storage_), target_config);
} }
tiered_config_ = rebuilt.config(); tiered_config_ = rebuilt.config();
storage_.template emplace<tiered_storage>(std::move(rebuilt)); storage_.template emplace<tiered_storage>(std::move(rebuilt));
@@ -1028,6 +1364,7 @@ private:
[[no_unique_address]] Equal equal_{}; [[no_unique_address]] Equal equal_{};
ResidencyMode residency_ = ResidencyMode::automatic; ResidencyMode residency_ = ResidencyMode::automatic;
ReadAdaptationMode read_adaptation_ = ReadAdaptationMode::deferred; ReadAdaptationMode read_adaptation_ = ReadAdaptationMode::deferred;
size_type logical_capacity_ = 0;
std::size_t read_sample_rate_ = 256; std::size_t read_sample_rate_ = 256;
mutable std::size_t read_sample_countdown_ = 256; mutable std::size_t read_sample_countdown_ = 256;
std::size_t edit_sample_rate_ = 1; std::size_t edit_sample_rate_ = 1;
+152 -30
View File
@@ -6,6 +6,7 @@
#include <limits> #include <limits>
#include <optional> #include <optional>
#include <stdexcept> #include <stdexcept>
#include <type_traits>
#include <utility> #include <utility>
#include <vector> #include <vector>
@@ -38,14 +39,39 @@ public:
} }
Entry& ensure(const Key& key) { Entry& ensure(const Key& key) {
maybe_grow(); // A moved-from std::vector is allowed to be empty while the scalar
const auto [position, found] = find_insert_position(key); // counters retain their old values. Rebuild lazily so a moved-from
// index remains reusable and mask() is never evaluated for zero
// buckets.
if (buckets_.empty()) {
rehash(16);
}
auto [position, found] = find_insert_position(key);
if (position == npos) {
// Normal growth is performed by reserve_for_elements() at a
// logical container-capacity change. Reaching a completely full
// table means that invariant was unavailable (most notably after
// reusing a moved-from standalone index), so grow only as a
// recovery path rather than as a load-factor policy.
if (buckets_.size() > std::numeric_limits<std::size_t>::max() / 2) {
throw std::length_error("flat hash index capacity overflow");
}
rehash(buckets_.size() * 2);
const auto retry = find_insert_position(key);
position = retry.first;
found = retry.second;
if (position == npos) {
throw std::length_error("flat hash index is full");
}
}
auto& bucket = buckets_[position]; auto& bucket = buckets_[position];
if (!found) { if (!found) {
bucket.key.emplace(key);
if (bucket.state == State::tombstone) { if (bucket.state == State::tombstone) {
--tombstones_; --tombstones_;
} }
bucket.key.emplace(key);
bucket.entry = {}; bucket.entry = {};
bucket.state = State::occupied; bucket.state = State::occupied;
++size_; ++size_;
@@ -58,27 +84,62 @@ public:
if (position == npos) { if (position == npos) {
return false; return false;
} }
auto& bucket = buckets_[position]; if constexpr (std::is_nothrow_invocable_v<const Hash&, const Key&>
bucket.key.reset(); && std::is_nothrow_move_assignable_v<Bucket>) {
bucket.entry = {}; erase_and_compact(position);
bucket.state = State::tombstone; } else {
--size_; // Computing hashes or moving a key can throw. Rebuild a complete
++tombstones_; // replacement without the erased key, then commit with swap, so a
if (tombstones_ > buckets_.size() / 4) { // failed erase never opens a hole in the live probe chain.
rehash(buckets_.size()); erase_transactional(position);
} }
--size_;
return true; return true;
} }
void clear() { void clear() {
// Allocate before committing so a failed reset leaves the live table
// and its duplicate metadata untouched.
std::vector<Bucket> replacement(16);
buckets_.swap(replacement);
size_ = 0;
tombstones_ = 0;
}
// Used by the owning container after moving the live table elsewhere.
// Keeping zero buckets avoids an allocation in its noexcept move path;
// ensure()/reserve_for_elements() restore the minimum table lazily.
void reset_after_move() noexcept {
buckets_.clear(); buckets_.clear();
size_ = 0; size_ = 0;
tombstones_ = 0; tombstones_ = 0;
rehash(16);
} }
[[nodiscard]] std::size_t distinct_values() const noexcept { return size_; } [[nodiscard]] std::size_t distinct_values() const noexcept { return size_; }
[[nodiscard]] std::size_t bucket_count() const noexcept { return buckets_.size(); } [[nodiscard]] std::size_t bucket_count() const noexcept { return buckets_.size(); }
[[nodiscard]] static std::size_t buckets_for_elements(
std::size_t element_capacity) {
// At most one distinct key per element. Keeping the worst-case load
// below 70% guarantees that ensure() cannot trigger an independent
// rehash between container capacity changes.
const auto required = element_capacity > (std::numeric_limits<std::size_t>::max() - 6) / 10
? std::numeric_limits<std::size_t>::max()
: (element_capacity * 10 + 6) / 7;
std::size_t buckets = 16;
while (buckets < required) {
if (buckets > std::numeric_limits<std::size_t>::max() / 2) {
throw std::length_error("flat hash index capacity overflow");
}
buckets *= 2;
}
return buckets;
}
void reserve_for_elements(std::size_t element_capacity) {
const auto requested = buckets_for_elements(element_capacity);
if (requested != buckets_.size() || tombstones_ != 0) {
rehash(requested);
}
}
[[nodiscard]] std::size_t allocated_bytes() const noexcept { [[nodiscard]] std::size_t allocated_bytes() const noexcept {
return buckets_.capacity() * sizeof(Bucket); return buckets_.capacity() * sizeof(Bucket);
} }
@@ -116,10 +177,20 @@ private:
[[nodiscard]] std::pair<std::size_t, bool> [[nodiscard]] std::pair<std::size_t, bool>
find_insert_position(const Key& key) const { find_insert_position(const Key& key) const {
auto position = hasher_(key) & mask(); return find_insert_position_in(buckets_, key);
}
[[nodiscard]] std::pair<std::size_t, bool>
find_insert_position_in(const std::vector<Bucket>& buckets,
const Key& key) const {
if (buckets.empty()) {
return {npos, false};
}
const auto table_mask = buckets.size() - 1;
auto position = hasher_(key) & table_mask;
auto first_tombstone = npos; auto first_tombstone = npos;
for (std::size_t probe = 0; probe < buckets_.size(); ++probe) { for (std::size_t probe = 0; probe < buckets.size(); ++probe) {
const auto& bucket = buckets_[position]; const auto& bucket = buckets[position];
if (bucket.state == State::empty) { if (bucket.state == State::empty) {
return {first_tombstone == npos ? position : first_tombstone, false}; return {first_tombstone == npos ? position : first_tombstone, false};
} }
@@ -130,41 +201,92 @@ private:
} else if (equal_(*bucket.key, key)) { } else if (equal_(*bucket.key, key)) {
return {position, true}; return {position, true};
} }
position = (position + 1) & mask(); position = (position + 1) & table_mask;
} }
if (first_tombstone != npos) { if (first_tombstone != npos) {
return {first_tombstone, false}; return {first_tombstone, false};
} }
throw std::length_error("flat hash index is full"); return {npos, false};
} }
void maybe_grow() { void erase_and_compact(std::size_t position) {
if ((size_ + tombstones_ + 1) * 100 >= buckets_.size() * 82) { auto hole = position;
rehash(buckets_.size() * 2); buckets_[hole] = Bucket{};
auto current = (hole + 1) & mask();
while (buckets_[current].state != State::empty) {
auto& bucket = buckets_[current];
if (bucket.state == State::occupied) {
const auto home = hasher_(*bucket.key) & mask();
const auto current_distance = (current - home) & mask();
const auto hole_distance = (hole - home) & mask();
if (hole_distance < current_distance) {
buckets_[hole] = std::move(bucket);
bucket = Bucket{};
hole = current;
} }
} }
current = (current + 1) & mask();
}
}
void erase_transactional(std::size_t erased_position) {
std::vector<Bucket> replacement(buckets_.size());
for (std::size_t source = 0; source < buckets_.size(); ++source) {
const auto& bucket = buckets_[source];
if (source == erased_position || bucket.state != State::occupied) {
continue;
}
const auto [position, found] =
find_insert_position_in(replacement, *bucket.key);
if (position == npos || found) {
throw std::logic_error("flat hash index rebuild invariant violated");
}
auto& target = replacement[position];
target.key.emplace(*bucket.key);
target.entry = bucket.entry;
target.state = State::occupied;
}
buckets_.swap(replacement);
tombstones_ = 0;
}
void rehash(std::size_t requested_capacity) { void rehash(std::size_t requested_capacity) {
std::size_t capacity = 16; std::size_t capacity = 16;
while (capacity < requested_capacity) { while (capacity < requested_capacity) {
if (capacity > std::numeric_limits<std::size_t>::max() / 2) {
throw std::length_error("flat hash index capacity overflow");
}
capacity *= 2; capacity *= 2;
} }
auto old = std::move(buckets_);
buckets_.assign(capacity, Bucket{}); // Build the complete replacement before touching the live table. An
size_ = 0; // allocation, hash/equality call, or copy of a copyable Key may throw;
tombstones_ = 0; // in all of those cases the original buckets and counters remain
for (auto& bucket : old) { // unchanged. vector::swap is the no-throw commit for std::allocator.
std::vector<Bucket> replacement(capacity);
std::size_t replacement_size = 0;
for (const auto& bucket : buckets_) {
if (bucket.state != State::occupied) { if (bucket.state != State::occupied) {
continue; continue;
} }
const auto [position, found] = find_insert_position(*bucket.key); const auto [position, found] =
(void)found; find_insert_position_in(replacement, *bucket.key);
auto& target = buckets_[position]; if (position == npos) {
target.key.emplace(std::move(*bucket.key)); throw std::length_error("flat hash index is full");
}
if (found) {
throw std::logic_error("flat hash index contains duplicate keys");
}
auto& target = replacement[position];
target.key.emplace(*bucket.key);
target.entry = bucket.entry; target.entry = bucket.entry;
target.state = State::occupied; target.state = State::occupied;
++size_; ++replacement_size;
} }
buckets_.swap(replacement);
size_ = replacement_size;
tombstones_ = 0;
} }
Hash hasher_{}; Hash hasher_{};
+88 -12
View File
@@ -231,6 +231,31 @@ public:
} }
} }
template <class Predicate>
[[nodiscard]] std::size_t find_if(Predicate&& predicate) const {
std::size_t logical = 0;
for (const auto& leaf : leaves_) {
for (std::size_t i = 0; i < leaf->values.size(); ++i, ++logical) {
if (predicate(leaf->values[i])) {
return logical;
}
}
}
return npos;
}
template <class Predicate, class Function>
void for_each_match(Predicate&& predicate, Function&& function) const {
std::size_t logical = 0;
for (const auto& leaf : leaves_) {
for (std::size_t i = 0; i < leaf->values.size(); ++i, ++logical) {
if (predicate(leaf->values[i])) {
function(logical, leaf->values[i]);
}
}
}
}
template <class Function> template <class Function>
void for_each_with_location(Function&& function) const { void for_each_with_location(Function&& function) const {
for (const auto& leaf : leaves_) { for (const auto& leaf : leaves_) {
@@ -262,21 +287,22 @@ public:
if (source.empty()) { if (source.empty()) {
return result; return result;
} }
const auto target_occupancy = bulk_target_occupancy( // Allocate the complete destination, including its directory, before
result.config_.leaf_capacity); // moving the first value. For nothrow-move T the following loop and
const auto count = (source.size() + target_occupancy - 1) // the final TieredStorage move cannot fail, so an allocation failure
/ target_occupancy; // leaves source completely untouched.
result.leaves_.reserve(count); const auto target_occupancy = result.prepare_bulk_destination(source.size());
std::size_t leaf_index = 0;
for (auto& value : source) { for (auto& value : source) {
if (result.leaves_.empty() auto& leaf = *result.leaves_[leaf_index];
|| result.leaves_.back()->values.size() == target_occupancy) { leaf.values.push_back(std::move(value));
result.leaves_.push_back(result.make_leaf());
}
result.leaves_.back()->values.push_back(std::move(value));
++result.size_; ++result.size_;
if (leaf.values.size() == target_occupancy
&& leaf_index + 1 < result.leaves_.size()) {
++leaf_index;
} }
result.rebuild_positions(); }
result.rebuild_directory(); assert(result.directory_.total() == result.size_);
for (std::size_t leaf = 0; leaf < result.leaves_.size(); ++leaf) { for (std::size_t leaf = 0; leaf < result.leaves_.size(); ++leaf) {
result.refresh_leaf(leaf, relocate); result.refresh_leaf(leaf, relocate);
} }
@@ -335,6 +361,31 @@ public:
return result; return result;
} }
// Rebuild a tiered layout without an intermediate vector. All leaves and
// directory arrays are allocated first; consequently this operation has a
// strong allocation-failure guarantee when T is nothrow-move-constructible.
static TieredStorage reconfigured_move(TieredStorage& source,
TieredConfig config) {
TieredStorage result(config);
if (source.empty()) {
return result;
}
const auto target_occupancy = result.prepare_bulk_destination(source.size());
std::size_t leaf_index = 0;
source.for_each([&](T& value) {
auto& leaf = *result.leaves_[leaf_index];
leaf.values.push_back(std::move(value));
++result.size_;
if (leaf.values.size() == target_occupancy
&& leaf_index + 1 < result.leaves_.size()) {
++leaf_index;
}
});
assert(result.directory_.total() == result.size_);
return result;
}
[[nodiscard]] std::size_t logical_index(leaf_id_type leaf_id, [[nodiscard]] std::size_t logical_index(leaf_id_type leaf_id,
std::size_t local) const { std::size_t local) const {
if (leaf_id >= leaf_positions_.size()) { if (leaf_id >= leaf_positions_.size()) {
@@ -387,6 +438,31 @@ private:
return std::max<std::size_t>(1, capacity - capacity / 8); return std::max<std::size_t>(1, capacity - capacity / 8);
} }
// Construct the entire shape needed by a bulk load while source elements
// are still untouched. RingBlock allocates all of its optional slots in
// its constructor, so inserting into these leaves does not allocate.
[[nodiscard]] std::size_t prepare_bulk_destination(std::size_t element_count) {
assert(element_count != 0);
const auto target_occupancy = bulk_target_occupancy(config_.leaf_capacity);
const auto count = 1 + (element_count - 1) / target_occupancy;
leaves_.reserve(count);
leaf_positions_.reserve(count);
std::vector<std::size_t> expected_sizes;
expected_sizes.reserve(count);
auto remaining = element_count;
for (std::size_t i = 0; i < count; ++i) {
leaves_.push_back(make_leaf());
const auto leaf_size = std::min(remaining, target_occupancy);
expected_sizes.push_back(leaf_size);
remaining -= leaf_size;
}
rebuild_positions();
directory_.rebuild(expected_sizes);
return target_occupancy;
}
[[nodiscard]] std::unique_ptr<Leaf> make_leaf() { [[nodiscard]] std::unique_ptr<Leaf> make_leaf() {
if (next_leaf_id_ == std::numeric_limits<leaf_id_type>::max()) { if (next_leaf_id_ == std::numeric_limits<leaf_id_type>::max()) {
throw std::length_error("tiered leaf id space exhausted"); throw std::length_error("tiered leaf id space exhausted");
@@ -0,0 +1,82 @@
# Focused resize benchmark — 2026-08-13
This run replaces the retired benchmark matrix with one size sweep and one
fixed workload. Every candidate is `AdaptiveSequence<uint32_t, true>` and uses
the same flat hash index; only storage residency differs.
Workload per mixed trace:
- 97,000 random indexed reads (97%);
- 1,000 `find_one` calls through the flat hash index (1%, 500 hit / 500 miss);
- 1,000 random inserts (1%);
- 1,000 random erases (1%).
Each cell is the median of seven paired repeats. Candidate order is rotated per
repeat. Construction is outside the timer; automatic conversion on a resize
boundary is inside it. The per-operation columns are separate batched traces,
while `mixed` is the requested end-to-end workload.
Environment: AMD Ryzen 9 5900X (12 cores / 24 threads), Windows 11 Pro build
26200, MSVC 19.51.36248, x64 Release `/O2 /MT`, baseline ISA profile, no IPO/LTO.
Command:
```powershell
.\out\bin\baseline\Release\uc_focused_bench.exe `
--operations 100000 --repeats 7 `
--output results\benchmarks\20260813-focused-resize\baseline.csv
```
## Median throughput (operations/second)
| n | container | mixed mode | mixed leaf | mixed ops/s | indexed read ops/s | hash find ops/s | insert ops/s | erase ops/s |
|---:|:----------|:-----------|-----------:|------------:|-------------------:|----------------:|-------------:|------------:|
| 256 | forced vector + hash | vector | 16 | 164,500,740 | 654,450,262 | 145,369,967 | 1,739,433 | 8,205,128 |
| 256 | forced tiered + hash | tiered | 17 | 41,291,601 | 54,182,922 | 81,479,671 | 2,459,420 | 5,203,252 |
| 256 | adaptive + hash | vector | 16 | 151,676,020 | 571,755,289 | 146,134,736 | 1,740,644 | 8,421,053 |
| 512 | forced vector + hash | vector | 23 | 103,412,616 | 657,030,223 | 129,584,035 | 1,358,880 | 5,014,691 |
| 512 | forced tiered + hash | tiered | 23 | 38,755,184 | 50,032,521 | 78,963,992 | 2,698,327 | 5,344,468 |
| 512 | adaptive + hash | vector | 23 | 98,328,417 | 575,373,993 | 129,098,890 | 1,300,221 | 5,256,674 |
| 1,024 | forced vector + hash | vector | 32 | 58,816,610 | 662,251,656 | 110,411,836 | 931,619 | 2,801,120 |
| 1,024 | forced tiered + hash | tiered | 33 | 35,664,610 | 46,172,315 | 71,118,697 | 3,144,654 | 4,748,338 |
| 1,024 | adaptive + hash | vector | 32 | 56,631,555 | 584,112,150 | 109,673,174 | 911,743 | 2,893,519 |
| 2,048 | forced vector + hash | vector | 46 | 31,979,533 | 654,878,847 | 81,426,594 | 570,125 | 973,615 |
| 2,048 | forced tiered + hash | tiered | 46 | 32,177,103 | 43,327,556 | 59,157,596 | 2,581,311 | 3,880,481 |
| 2,048 | adaptive + hash | vector | 46 | 31,330,284 | 580,383,053 | 81,406,708 | 565,835 | 995,619 |
| 4,096 | forced vector + hash | vector | 64 | 17,199,862 | 661,813,369 | 77,297,673 | 313,342 | 421,248 |
| 4,096 | forced tiered + hash | tiered | 65 | 32,526,672 | 45,460,745 | 55,303,617 | 1,793,079 | 3,138,732 |
| 4,096 | adaptive + hash | vector → tiered | 65 | 32,639,206 | 581,395,349 | 76,710,647 | 1,822,822 | 422,048 |
| 8,192 | forced vector + hash | vector | 91 | 8,791,518 | 657,894,737 | 72,385,089 | 164,395 | 198,736 |
| 8,192 | forced tiered + hash | tiered | 91 | 28,479,481 | 42,929,510 | 51,116,904 | 1,317,003 | 2,276,867 |
| 8,192 | adaptive + hash | tiered | 91 | 28,603,301 | 40,546,568 | 50,296,751 | 1,348,618 | 2,704,896 |
| 16,384 | forced vector + hash | vector | 128 | 4,489,580 | 649,350,649 | 79,478,620 | 86,972 | 96,230 |
| 16,384 | forced tiered + hash | tiered | 129 | 23,255,814 | 40,364,899 | 51,284,681 | 935,629 | 1,709,694 |
| 16,384 | adaptive + hash | tiered | 129 | 22,955,260 | 38,213,153 | 50,658,561 | 836,400 | 2,076,843 |
| 32,768 | forced vector + hash | vector | 182 | 2,246,076 | 610,873,549 | 79,687,625 | 43,018 | 47,784 |
| 32,768 | forced tiered + hash | tiered | 182 | 18,402,989 | 37,572,797 | 50,266,412 | 571,004 | 1,245,640 |
| 32,768 | adaptive + hash | tiered | 182 | 17,943,013 | 35,944,071 | 49,058,085 | 514,165 | 1,548,227 |
| 65,536 | forced vector + hash | vector | 256 | 1,089,099 | 490,436,488 | 63,678,044 | 21,507 | 22,895 |
| 65,536 | forced tiered + hash | tiered | 257 | 12,614,158 | 32,799,790 | 44,620,945 | 315,139 | 910,167 |
| 65,536 | adaptive + hash | tiered | 257 | 12,777,430 | 30,628,809 | 42,513,392 | 295,255 | 1,116,819 |
## Crossover
At `n=2,048`, forced tiered had only a 0.618% median advantage and won four of
seven paired repeats (forced vector won three), so this boundary is treated as
noise rather than a stable crossover. At `n=4,096`, forced tiered won all seven
paired repeats and its median throughput was 1.8911x that of forced vector.
The adaptive row includes the one-time `vector → tiered` conversion and still
reached 32.64M mixed ops/s, essentially matching already-tiered storage.
Therefore the calibrated resize-boundary cutoff is **4,096 elements**, the
first decisive and stable size in this sweep.
At exactly 4,096 elements an adaptive container is still vector. The next
insert changes logical capacity from 4,096 to 8,192 and performs the conversion;
the final leaf is `ceil(sqrt(4097)) = 65`. In the separate read/find/erase
batches no growth occurs, so those adaptive 4,096 measurements remain vector;
the insert batch and mixed trace include the conversion.
All 189 raw rows have the required operation counts, and checksums match across
the three candidates for every `(n, repeat)` pair. The CSV also records the
mode and leaf used by each separate read/find/insert/erase batch. Raw data is in
[`baseline.csv`](baseline.csv).
@@ -0,0 +1,190 @@
profile,n,container,repeat,seed,initial_mode,mixed_mode,initial_leaf,mixed_leaf,initial_logical_capacity,logical_capacity,initial_hash_buckets,hash_buckets,operations,read_count,find_count,find_hit_count,find_miss_count,insert_count,erase_count,read_batch_count,find_batch_count,insert_batch_count,erase_batch_count,read_mode,read_leaf,find_mode,find_leaf,insert_mode,insert_leaf,erase_mode,erase_leaf,mixed_ops_per_sec,read_ops_per_sec,find_ops_per_sec,insert_ops_per_sec,erase_ops_per_sec,bytes,checksum
"baseline",256,"forced_vector_hash",0,11643429248495452671,"vector","vector",16,16,256,512,512,1024,100000,97000,1000,500,500,1000,1000,100000,100000,1000,256,"vector",16,"vector",16,"vector",16,"vector",16,167168171.18020728,654450261.78010476,148367952.52225518,1816200.5085361423,8101265.8227848094,39456,2516218684603091152
"baseline",256,"forced_tiered_hash",0,11643429248495452671,"tiered","tiered",16,17,256,512,512,1024,100000,97000,1000,500,500,1000,1000,100000,100000,1000,256,"tiered",16,"tiered",16,"tiered",33,"tiered",4,42591251.756889135,54531573.781219326,82209799.408089444,2222716.159146477,4620938.628158845,43752,2516218684603091152
"baseline",256,"adaptive_hash",0,11643429248495452671,"vector","vector",16,16,256,512,512,1024,100000,97000,1000,500,500,1000,1000,100000,100000,1000,256,"vector",16,"vector",16,"vector",16,"vector",16,158579130.98636219,583430571.76196039,147167034.58425313,1857010.2135561744,8258064.5161290318,39456,2516218684603091152
"baseline",256,"forced_vector_hash",1,15449987037075203961,"vector","vector",16,16,256,512,512,1024,100000,97000,1000,500,500,1000,1000,100000,100000,1000,256,"vector",16,"vector",16,"vector",16,"vector",16,150172698.60339391,637755102.04081643,143636885.95231256,1678697.330871244,8050314.4654088048,39456,2515907659441309718
"baseline",256,"forced_tiered_hash",1,15449987037075203961,"tiered","tiered",16,17,256,512,512,1024,100000,97000,1000,500,500,1000,1000,100000,100000,1000,256,"tiered",16,"tiered",16,"tiered",33,"tiered",4,41291601.288297959,52706477.62610025,80392314.494734302,2383790.2264600713,5192697.7687626779,44132,2515907659441309718
"baseline",256,"adaptive_hash",1,15449987037075203961,"vector","vector",16,16,256,512,512,1024,100000,97000,1000,500,500,1000,1000,100000,100000,1000,256,"vector",16,"vector",16,"vector",16,"vector",16,149320591.30954158,567536889.89784336,143533802.21042055,1709109.5539224066,8126984.1269841269,39456,2515907659441309718
"baseline",256,"forced_tiered_hash",2,18085065223655675095,"tiered","tiered",16,17,256,512,512,1024,100000,97000,1000,500,500,1000,1000,100000,100000,1000,256,"tiered",16,"tiered",16,"tiered",33,"tiered",4,39816842.524387822,53231129.564569362,78876794.447073683,2263980.0769753228,5203252.0325203249,43752,2516672311691009245
"baseline",256,"adaptive_hash",2,18085065223655675095,"vector","vector",16,16,256,512,512,1024,100000,97000,1000,500,500,1000,1000,100000,100000,1000,256,"vector",16,"vector",16,"vector",16,"vector",16,153186274.50980392,557103064.06685233,138715494.52073798,1654533.421575116,8619528.6195286196,39456,2516672311691009245
"baseline",256,"forced_vector_hash",2,18085065223655675095,"vector","vector",16,16,256,512,512,1024,100000,97000,1000,500,500,1000,1000,100000,100000,1000,256,"vector",16,"vector",16,"vector",16,"vector",16,169262017.60324982,660066006.60066009,143595634.69270533,1761804.0873854826,9045936.3957597166,39456,2516672311691009245
"baseline",256,"adaptive_hash",3,9610393720456262672,"vector","vector",16,16,256,512,512,1024,100000,97000,1000,500,500,1000,1000,100000,100000,1000,256,"vector",16,"vector",16,"vector",16,"vector",16,151676020.02123463,571755288.73642087,146584579.30225742,1740644.0382941687,9078014.1843971629,39456,2516336871995962384
"baseline",256,"forced_vector_hash",3,9610393720456262672,"vector","vector",16,16,256,512,512,1024,100000,97000,1000,500,500,1000,1000,100000,100000,1000,256,"vector",16,"vector",16,"vector",16,"vector",16,166002656.04249668,660501981.50594449,147623265.42663124,1699235.344095157,8476821.1920529809,39456,2516336871995962384
"baseline",256,"forced_tiered_hash",3,9610393720456262672,"tiered","tiered",16,17,256,512,512,1024,100000,97000,1000,500,500,1000,1000,100000,100000,1000,256,"tiered",16,"tiered",16,"tiered",33,"tiered",4,42925824.17582418,54182921.543129608,81479670.822129875,2552322.6135783563,5423728.8135593217,43920,2516336871995962384
"baseline",256,"forced_vector_hash",4,13992777055088439294,"vector","vector",16,16,256,512,512,1024,100000,97000,1000,500,500,1000,1000,100000,100000,1000,256,"vector",16,"vector",16,"vector",16,"vector",16,150693188.66787222,639795265.51503515,145369966.5649077,1670564.650851988,8205128.205128205,39456,2516698023620607488
"baseline",256,"forced_tiered_hash",4,13992777055088439294,"tiered","tiered",16,17,256,512,512,1024,100000,97000,1000,500,500,1000,1000,100000,100000,1000,256,"tiered",16,"tiered",16,"tiered",33,"tiered",4,39794659.556687489,53524594.551196277,81208380.704888746,2526528.5497726123,5192697.7687626779,44132,2516698023620607488
"baseline",256,"adaptive_hash",4,13992777055088439294,"vector","vector",16,16,256,512,512,1024,100000,97000,1000,500,500,1000,1000,100000,100000,1000,256,"vector",16,"vector",16,"vector",16,"vector",16,160000000,576701268.74279118,141944641.58977997,1837897.4453225508,8504983.3887043186,39456,2516698023620607488
"baseline",256,"forced_tiered_hash",5,5937878447685887746,"tiered","tiered",16,17,256,512,512,1024,100000,97000,1000,500,500,1000,1000,100000,100000,1000,256,"tiered",16,"tiered",16,"tiered",33,"tiered",4,42665756.463862106,55075177.617447816,83008217.813563541,2522068.0958385877,5481798.7152034268,43484,2516157113323081812
"baseline",256,"adaptive_hash",5,5937878447685887746,"vector","vector",16,16,256,512,512,1024,100000,97000,1000,500,500,1000,1000,100000,100000,1000,256,"vector",16,"vector",16,"vector",16,"vector",16,133886731.82487616,569800569.80056977,146649068.77841324,1596424.0102171137,8421052.6315789465,39456,2516157113323081812
"baseline",256,"forced_vector_hash",5,5937878447685887746,"vector","vector",16,16,256,512,512,1024,100000,97000,1000,500,500,1000,1000,100000,100000,1000,256,"vector",16,"vector",16,"vector",16,"vector",16,164500740.25333115,650618087.18282366,143430866.32243258,1739432.9448599755,8648648.6486486476,39456,2516157113323081812
"baseline",256,"forced_tiered_hash",6,11237391578505003154,"tiered","tiered",16,17,256,512,512,1024,100000,97000,1000,500,500,1000,1000,100000,100000,1000,256,"tiered",16,"tiered",16,"tiered",33,"tiered",4,40705010.786827855,54262303.977426879,83654007.026936576,2459419.5769798327,5481798.7152034268,44400,2516652954639355697
"baseline",256,"adaptive_hash",6,11237391578505003154,"vector","vector",16,16,256,512,512,1024,100000,97000,1000,500,500,1000,1000,100000,100000,1000,256,"vector",16,"vector",16,"vector",16,"vector",16,133440085.40165466,573723465.28973031,146134736.22680113,1777461.7845716316,7734138.9728096677,39456,2516652954639355697
"baseline",256,"forced_vector_hash",6,11237391578505003154,"vector","vector",16,16,256,512,512,1024,100000,97000,1000,500,500,1000,1000,100000,100000,1000,256,"vector",16,"vector",16,"vector",16,"vector",16,141302811.92595732,660938532.71645737,147492625.36873156,1755926.2510974538,7619047.6190476194,39456,2516652954639355697
"baseline",512,"forced_tiered_hash",0,14984970845100107740,"tiered","tiered",23,23,512,1024,1024,2048,100000,97000,1000,500,500,1000,1000,100000,100000,1000,512,"tiered",23,"tiered",23,"tiered",33,"tiered",4,39111389.236545682,50032521.138740182,78727759.407967255,2598077.4227071968,5289256.1983471075,84724,2516449983142523160
"baseline",512,"adaptive_hash",0,14984970845100107740,"vector","vector",23,23,512,1024,1024,2048,100000,97000,1000,500,500,1000,1000,100000,100000,1000,512,"vector",23,"vector",23,"vector",23,"vector",23,94822681.585435241,576701268.74279118,130225289.7512697,1323801.9592268995,5378151.260504202,78368,2516449983142523160
"baseline",512,"forced_vector_hash",0,14984970845100107740,"vector","vector",23,23,512,1024,1024,2048,100000,97000,1000,500,500,1000,1000,100000,100000,1000,512,"vector",23,"vector",23,"vector",23,"vector",23,98463962.189838529,651890482.39895701,130718954.24836603,1371930.3059404583,5251282.051282051,78368,2516449983142523160
"baseline",512,"adaptive_hash",1,16304051715571137494,"vector","vector",23,23,512,1024,1024,2048,100000,97000,1000,500,500,1000,1000,100000,100000,1000,512,"vector",23,"vector",23,"vector",23,"vector",23,98328416.912487715,567859170.92561042,129065565.30717605,1272264.631043257,5517241.3793103443,78368,2516668772023801606
"baseline",512,"forced_vector_hash",1,16304051715571137494,"vector","vector",23,23,512,1024,1024,2048,100000,97000,1000,500,500,1000,1000,100000,100000,1000,512,"vector",23,"vector",23,"vector",23,"vector",23,102301790.28132991,652741514.3603133,129584035.2468576,1286670.0977869274,4543034.6051464062,78368,2516668772023801606
"baseline",512,"forced_tiered_hash",1,16304051715571137494,"tiered","tiered",23,23,512,1024,1024,2048,100000,97000,1000,500,500,1000,1000,100000,100000,1000,512,"tiered",23,"tiered",23,"tiered",33,"tiered",4,37228695.878783368,49842994.567113593,79170295.305201486,2661698.163428267,5366876.3102725362,85064,2516668772023801606
"baseline",512,"forced_vector_hash",2,9563555999342315634,"vector","vector",23,23,512,1024,1024,2048,100000,97000,1000,500,500,1000,1000,100000,100000,1000,512,"vector",23,"vector",23,"vector",23,"vector",23,103412616.33919339,657894736.84210527,130907186.80455558,1358880.2826470986,5135406.2186559681,78368,2516540277992719419
"baseline",512,"forced_tiered_hash",2,9563555999342315634,"tiered","tiered",23,23,512,1024,1024,2048,100000,97000,1000,500,500,1000,1000,100000,100000,1000,512,"tiered",23,"tiered",23,"tiered",33,"tiered",4,38556446.637877852,50152966.547971316,78963992.419456735,2784739.62684489,5344467.6409185808,85404,2516540277992719419
"baseline",512,"adaptive_hash",2,9563555999342315634,"vector","vector",23,23,512,1024,1024,2048,100000,97000,1000,500,500,1000,1000,100000,100000,1000,512,"vector",23,"vector",23,"vector",23,"vector",23,94366330.093422666,575373993.09551215,130174433.74121322,1284026.7077555214,4361158.4327086881,78368,2516540277992719419
"baseline",512,"adaptive_hash",3,14934861932824753028,"vector","vector",23,23,512,1024,1024,2048,100000,97000,1000,500,500,1000,1000,100000,100000,1000,512,"vector",23,"vector",23,"vector",23,"vector",23,93870271.285084024,575705238.91767418,131061598.95150721,1336183.8588989845,5140562.248995984,78368,2516478738380764097
"baseline",512,"forced_vector_hash",3,14934861932824753028,"vector","vector",23,23,512,1024,1024,2048,100000,97000,1000,500,500,1000,1000,100000,100000,1000,512,"vector",23,"vector",23,"vector",23,"vector",23,101327388.79319081,650618087.18282366,127860887.35455824,1337613.6971642592,4530973.4513274338,78368,2516478738380764097
"baseline",512,"forced_tiered_hash",3,14934861932824753028,"tiered","tiered",23,23,512,1024,1024,2048,100000,97000,1000,500,500,1000,1000,100000,100000,1000,512,"tiered",23,"tiered",23,"tiered",33,"tiered",4,38090884.851255096,49756194.646233454,78143314.839415491,2698327.0372369131,5262076.0534429606,86592,2516478738380764097
"baseline",512,"adaptive_hash",4,7237444903719400313,"vector","vector",23,23,512,1024,1024,2048,100000,97000,1000,500,500,1000,1000,100000,100000,1000,512,"vector",23,"vector",23,"vector",23,"vector",23,105596620.90813093,577700751.01097631,126582278.48101266,1300221.0375763881,5256673.5112936348,78368,2516642161409729628
"baseline",512,"forced_vector_hash",4,7237444903719400313,"vector","vector",23,23,512,1024,1024,2048,100000,97000,1000,500,500,1000,1000,100000,100000,1000,512,"vector",23,"vector",23,"vector",23,"vector",23,107054919.17353603,657030223.39027584,125203455.615375,1225940.9096481549,5283797.7296181628,78368,2516642161409729628
"baseline",512,"forced_tiered_hash",4,7237444903719400313,"tiered","tiered",23,23,512,1024,1024,2048,100000,97000,1000,500,500,1000,1000,100000,100000,1000,512,"tiered",23,"tiered",23,"tiered",33,"tiered",4,38755183.505793899,50877639.277537525,78963992.419456735,2645502.6455026455,5355648.5355648529,86252,2516642161409729628
"baseline",512,"adaptive_hash",5,14701316826153139949,"vector","vector",23,23,512,1024,1024,2048,100000,97000,1000,500,500,1000,1000,100000,100000,1000,512,"vector",23,"vector",23,"vector",23,"vector",23,106405618.21664184,566893424.03628111,123823675.08667657,1296176.2799740764,5120000,78368,2516651054704929232
"baseline",512,"forced_vector_hash",5,14701316826153139949,"vector","vector",23,23,512,1024,1024,2048,100000,97000,1000,500,500,1000,1000,100000,100000,1000,512,"vector",23,"vector",23,"vector",23,"vector",23,115526802.2181146,664451827.24252486,121802679.65895249,1362397.8201634879,4172779.1361043192,78368,2516651054704929232
"baseline",512,"forced_tiered_hash",5,14701316826153139949,"tiered","tiered",23,23,512,1024,1024,2048,100000,97000,1000,500,500,1000,1000,100000,100000,1000,512,"tiered",23,"tiered",23,"tiered",33,"tiered",4,39354584.809130266,50597045.132564254,79333597.778659269,2932551.319648094,5372507.8698845748,85064,2516651054704929232
"baseline",512,"adaptive_hash",6,2747466510094964666,"vector","vector",23,23,512,1024,1024,2048,100000,97000,1000,500,500,1000,1000,100000,100000,1000,512,"vector",23,"vector",23,"vector",23,"vector",23,103616205.57455187,569151963.57427442,129098889.74954817,1428775.5393627661,5294725.9565667007,78368,2532477225510751123
"baseline",512,"forced_vector_hash",6,2747466510094964666,"vector","vector",23,23,512,1024,1024,2048,100000,97000,1000,500,500,1000,1000,100000,100000,1000,512,"vector",23,"vector",23,"vector",23,"vector",23,104569695.70218551,662251655.62913907,133511348.46461949,1441337.5612568464,5014691.4789422136,78368,2532477225510751123
"baseline",512,"forced_tiered_hash",6,2747466510094964666,"tiered","tiered",23,23,512,1024,1024,2048,100000,97000,1000,500,500,1000,1000,100000,100000,1000,512,"tiered",23,"tiered",23,"tiered",33,"tiered",4,39379380.956131369,49183553.019870147,79808459.696727857,2887669.6505919723,5084409.1360476669,85744,2532477225510751123
"baseline",1024,"adaptive_hash",0,8263605205929953231,"vector","vector",32,32,1024,2048,2048,4096,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",32,"vector",32,"vector",32,"vector",32,61117222.833394453,584112149.53271031,114573785.51787351,934666.79128890554,2944640.7538280329,156192,2533248489645560685
"baseline",1024,"forced_vector_hash",0,8263605205929953231,"vector","vector",32,32,1024,2048,2048,4096,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",32,"vector",32,"vector",32,"vector",32,59220656.164870307,666666666.66666675,113623451.88046813,946073.79375591292,2983293.5560859186,156192,2533248489645560685
"baseline",1024,"forced_tiered_hash",0,8263605205929953231,"tiered","tiered",32,33,1024,2048,2048,4096,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",32,"tiered",32,"tiered",33,"tiered",6,35664610.007489569,45676700.31516923,70721357.85007073,3117206.9825436408,4653327.1288971612,164256,2533248489645560685
"baseline",1024,"forced_vector_hash",1,7082387021252559084,"vector","vector",32,32,1024,2048,2048,4096,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",32,"vector",32,"vector",32,"vector",32,58816609.810610518,662251655.62913907,112854079.67498025,931619.15408980811,2919708.0291970805,156192,2533011276197967348
"baseline",1024,"forced_tiered_hash",1,7082387021252559084,"tiered","tiered",32,33,1024,2048,2048,4096,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",32,"tiered",32,"tiered",33,"tiered",6,35463508.050216332,46358536.924574666,70982396.365701303,3224766.2044501775,4657661.8537494177,165176,2533011276197967348
"baseline",1024,"adaptive_hash",1,7082387021252559084,"vector","vector",32,32,1024,2048,2048,4096,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",32,"vector",32,"vector",32,"vector",32,56631555.102503113,583771161.70461178,108695652.17391305,911743.2530999271,2893518.5185185187,156192,2533011276197967348
"baseline",1024,"forced_tiered_hash",2,9982579891442255758,"tiered","tiered",32,33,1024,2048,2048,4096,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",32,"tiered",32,"tiered",33,"tiered",6,35683699.685983442,46957175.056348607,71352122.725651085,3155569.5803092457,4775549.1881566383,166772,2532625679719947189
"baseline",1024,"adaptive_hash",2,9982579891442255758,"vector","vector",32,32,1024,2048,2048,4096,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",32,"vector",32,"vector",32,"vector",32,58997050.147492632,589622641.50943398,109673173.94165386,888730.89228581579,2796420.5816554809,156192,2532625679719947189
"baseline",1024,"forced_vector_hash",2,9982579891442255758,"vector","vector",32,32,1024,2048,2048,4096,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",32,"vector",32,"vector",32,"vector",32,59648076.349537723,663129973.47480106,109349371.24111538,983284.1691248772,2395783.4211787255,156192,2532625679719947189
"baseline",1024,"forced_tiered_hash",3,6223662145476107803,"tiered","tiered",32,33,1024,2048,2048,4096,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",32,"tiered",32,"tiered",33,"tiered",6,35713010.249633946,46853769.385747083,71397972.297586754,3168567.8073510774,4866180.0486618001,166096,2532988634027205437
"baseline",1024,"adaptive_hash",3,6223662145476107803,"vector","vector",32,32,1024,2048,2048,4096,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",32,"vector",32,"vector",32,"vector",32,61591525.006159149,590667454.22327232,109613065.8774526,936154.27822505147,2962085.3080568719,156192,2532988634027205437
"baseline",1024,"forced_vector_hash",3,6223662145476107803,"vector","vector",32,32,1024,2048,2048,4096,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",32,"vector",32,"vector",32,"vector",32,60081711.127132908,658327847.26793945,109051254.08942203,971439.67359626968,2790178.5714285714,156192,2532988634027205437
"baseline",1024,"adaptive_hash",4,7077184263008060768,"vector","vector",32,32,1024,2048,2048,4096,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",32,"vector",32,"vector",32,"vector",32,51958848.591915205,584453535.94389248,113083795.09216329,926268.98851426458,2952465.308532625,156192,2533026764089274159
"baseline",1024,"forced_vector_hash",4,7077184263008060768,"vector","vector",32,32,1024,2048,2048,4096,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",32,"vector",32,"vector",32,"vector",32,55922156.358349182,670241286.86327076,113430127.0417423,912741.87659729831,2801120.4481792715,156192,2533026764089274159
"baseline",1024,"forced_tiered_hash",4,7077184263008060768,"tiered","tiered",32,33,1024,2048,2048,4096,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",32,"tiered",32,"tiered",33,"tiered",6,35472313.859033026,45400889.857441209,71751452.966922581,3144654.0880503147,4537205.081669692,164716,2533026764089274159
"baseline",1024,"forced_tiered_hash",5,3198208010137807867,"tiered","tiered",32,33,1024,2048,2048,4096,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",32,"tiered",32,"tiered",33,"tiered",6,35835871.707579285,46172315.079878107,71118697.105469033,3102699.3484331369,4782400.7651841231,166096,2532977876410076447
"baseline",1024,"adaptive_hash",5,3198208010137807867,"vector","vector",32,32,1024,2048,2048,4096,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",32,"vector",32,"vector",32,"vector",32,52623270.009998426,307219662.05837172,81426593.925576091,751145.49688274611,2209944.7513812156,156192,2532977876410076447
"baseline",1024,"forced_vector_hash",5,3198208010137807867,"vector","vector",32,32,1024,2048,2048,4096,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",32,"vector",32,"vector",32,"vector",32,45793836.149654262,351988736.36043644,82994439.372562036,754147.81297134247,2220248.6678507994,156192,2532977876410076447
"baseline",1024,"adaptive_hash",6,878090231870985884,"vector","vector",32,32,1024,2048,2048,4096,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",32,"vector",32,"vector",32,"vector",32,47361939.945060156,540248514.31658566,110168557.89357717,882145.37755822157,2691790.0403768509,156192,2533145598879769717
"baseline",1024,"forced_vector_hash",6,878090231870985884,"vector","vector",32,32,1024,2048,2048,4096,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",32,"vector",32,"vector",32,"vector",32,57286892.758936755,656167979.00262475,110411836.14883517,918020.74726888817,2845759.8178713717,156192,2533145598879769717
"baseline",1024,"forced_tiered_hash",6,878090231870985884,"tiered","tiered",32,33,1024,2048,2048,4096,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",32,"tiered",32,"tiered",33,"tiered",6,34379619.761405438,44738725.841088042,69895855.175788075,2955956.251847473,4748338.0816714149,166772,2533145598879769717
"baseline",2048,"forced_tiered_hash",0,17604282621400283443,"tiered","tiered",46,46,2048,4096,4096,8192,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",46,"tiered",46,"tiered",46,"tiered",46,31505986.137366097,41753653.444676407,59154096.421177171,2295684.113865932,2778549.5971103082,321736,2555495986187765739
"baseline",2048,"adaptive_hash",0,17604282621400283443,"vector","vector",46,46,2048,4096,4096,8192,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",46,"vector",46,"vector",46,"vector",46,28851702.250432778,572409845.44934177,78758761.912262738,567086.31053646363,960891.7075045642,311840,2555495986187765739
"baseline",2048,"forced_vector_hash",0,17604282621400283443,"vector","vector",46,46,2048,4096,4096,8192,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",46,"vector",46,"vector",46,"vector",46,32079042.761364002,645161290.3225807,79107665.532790124,566829.15769187163,867077.0831526923,311840,2555495986187765739
"baseline",2048,"forced_tiered_hash",1,16081151116256482927,"tiered","tiered",46,46,2048,4096,4096,8192,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",46,"tiered",46,"tiered",46,"tiered",46,32177102.773666259,43327556.325823218,58768218.147625767,2600104.0041601663,3813882.5324180019,322968,2556698868577027992
"baseline",2048,"adaptive_hash",1,16081151116256482927,"vector","vector",46,46,2048,4096,4096,8192,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",46,"vector",46,"vector",46,"vector",46,31330283.852371704,580383052.81485784,81433224.755700335,568795.85916614521,995619.2751891677,311840,2556698868577027992
"baseline",2048,"forced_vector_hash",1,16081151116256482927,"vector","vector",46,46,2048,4096,4096,8192,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",46,"vector",46,"vector",46,"vector",46,31755104.633069765,661813368.63004637,81333875.559170395,575175.42850569426,978377.84952548682,311840,2556698868577027992
"baseline",2048,"forced_vector_hash",2,18436629033319334241,"vector","vector",46,46,2048,4096,4096,8192,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",46,"vector",46,"vector",46,"vector",46,31345014.575431775,651465798.04560268,82155767.334866896,570125.42759407067,973520.24922118383,311840,2556276914809095413
"baseline",2048,"forced_tiered_hash",2,18436629033319334241,"tiered","tiered",46,46,2048,4096,4096,8192,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",46,"tiered",46,"tiered",46,"tiered",46,32539372.640895486,43639537.420903333,59559261.465157837,2566735.1129363449,3907776.4751856197,323584,2556276914809095413
"baseline",2048,"adaptive_hash",2,18436629033319334241,"vector","vector",46,46,2048,4096,4096,8192,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",46,"vector",46,"vector",46,"vector",46,33111486.374623358,585823081.42940831,82290980.908492431,562651.21251336299,935016.36278634879,311840,2556276914809095413
"baseline",2048,"forced_vector_hash",3,5149219854132784233,"vector","vector",46,46,2048,4096,4096,8192,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",46,"vector",46,"vector",46,"vector",46,29173230.643561468,654878847.41322851,80619155.111254439,546716.96462741238,956022.94455066917,311840,2556998120563425210
"baseline",2048,"forced_tiered_hash",3,5149219854132784233,"tiered","tiered",46,46,2048,4096,4096,8192,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",46,"tiered",46,"tiered",46,"tiered",46,32399157.621901833,43691017.126878709,58210605.972408168,2511931.6754584275,3869969.0402476783,321736,2556998120563425210
"baseline",2048,"adaptive_hash",3,5149219854132784233,"vector","vector",46,46,2048,4096,4096,8192,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",46,"vector",46,"vector",46,"vector",46,28376039.272438351,578703703.70370376,81406707.91273202,542652.48534838296,1007759.7500755819,311840,2556998120563425210
"baseline",2048,"adaptive_hash",4,11228077710321852230,"vector","vector",46,46,2048,4096,4096,8192,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",46,"vector",46,"vector",46,"vector",46,30816640.98613251,569800569.80056977,80070462.006565779,579710.14492753625,1005732.6762546515,311840,2556450339210586434
"baseline",2048,"forced_vector_hash",4,11228077710321852230,"vector","vector",46,46,2048,4096,4096,8192,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",46,"vector",46,"vector",46,"vector",46,33212660.666245975,663570006.63570011,81426593.925576091,596338.48172222555,973615.03261610365,311840,2556450339210586434
"baseline",2048,"forced_tiered_hash",4,11228077710321852230,"tiered","tiered",46,46,2048,4096,4096,8192,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",46,"tiered",46,"tiered",46,"tiered",46,32150205.761316869,43252595.155709349,59157595.835305251,2615062.7615062762,3880481.1796662789,320608,2556450339210586434
"baseline",2048,"adaptive_hash",5,9566722756865290973,"vector","vector",46,46,2048,4096,4096,8192,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",46,"vector",46,"vector",46,"vector",46,31993857.179421552,587199060.48150325,82088327.039894924,558628.0096084018,965344.14518775942,311840,2555871518819976418
"baseline",2048,"forced_vector_hash",5,9566722756865290973,"vector","vector",46,46,2048,4096,4096,8192,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",46,"vector",46,"vector",46,"vector",46,32189531.964205239,669792364.36704624,83104795.146679968,547225.5663784612,975039.0015600624,311840,2555871518819976418
"baseline",2048,"forced_tiered_hash",5,9566722756865290973,"tiered","tiered",46,46,2048,4096,4096,8192,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",46,"tiered",46,"tiered",46,"tiered",46,31830914.183855359,42920297.008455291,59783583.427990675,2710027.1002710024,3894080.9968847353,320608,2555871518819976418
"baseline",2048,"adaptive_hash",6,11775567059054511224,"vector","vector",46,46,2048,4096,4096,8192,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",46,"vector",46,"vector",46,"vector",46,32073898.261594713,581395348.83720922,81162243.324405476,565834.88937927911,1044059.302568386,311840,2556823170109389014
"baseline",2048,"forced_vector_hash",6,11775567059054511224,"vector","vector",46,46,2048,4096,4096,8192,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",46,"vector",46,"vector",46,"vector",46,31979533.098816756,653167864.14108419,82304526.748971194,576202.82339383464,1034768.2119205298,311840,2556823170109389014
"baseline",2048,"forced_tiered_hash",6,11775567059054511224,"tiered","tiered",46,46,2048,4096,4096,8192,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",46,"tiered",46,"tiered",46,"tiered",46,33871896.487484336,43940592.31918446,59220656.164870307,2581311.3061435209,3938558.4875935409,321736,2556823170109389014
"baseline",4096,"forced_tiered_hash",0,7600465057368479647,"tiered","tiered",64,65,4096,8192,8192,16384,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",64,"tiered",64,"tiered",65,"tiered",64,32578595.862518322,46262028.1273131,54824561.403508767,1773678.60943597,3109452.736318408,628464,2606749384601431537
"baseline",4096,"adaptive_hash",0,7600465057368479647,"vector","tiered",64,65,4096,8192,8192,16384,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",64,"vector",64,"vector->tiered",65,"vector",64,32639206.21450486,581395348.83720922,76710647.437864378,1886080.7242549981,434839.32686872198,628464,2606749384601431537
"baseline",4096,"forced_vector_hash",0,7600465057368479647,"vector","vector",64,64,4096,8192,8192,16384,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",64,"vector",64,"vector",64,"vector",64,17404016.847088307,661813368.63004637,77297673.340032458,289435.60057887121,427880.70685892773,623136,2606749384601431537
"baseline",4096,"forced_vector_hash",1,7524986396680695453,"vector","vector",64,64,4096,8192,8192,16384,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",64,"vector",64,"vector",64,"vector",64,17641040.115725223,638569604.0868454,76341705.473700285,315537.04404897132,429239.81628535863,623136,2601740960506217425
"baseline",4096,"forced_tiered_hash",1,7524986396680695453,"tiered","tiered",64,65,4096,8192,8192,16384,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",64,"tiered",64,"tiered",65,"tiered",64,32450674.974039461,45363817.818907641,54030689.431597143,1796622.3499820339,3147623.5442241104,624244,2601740960506217425
"baseline",4096,"adaptive_hash",1,7524986396680695453,"vector","tiered",64,65,4096,8192,8192,16384,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",64,"vector",64,"vector->tiered",65,"vector",64,32852590.426755149,581057524.69494474,76359193.646915093,1822821.7280349983,426712.18263281416,624244,2601740960506217425
"baseline",4096,"forced_vector_hash",2,7460972931250716399,"vector","vector",64,64,4096,8192,8192,16384,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",64,"vector",64,"vector",64,"vector",64,17199862.401100792,666222518.32111931,78864353.312302843,299697.30572122155,380531.98371323111,623136,2608275452360560399
"baseline",4096,"forced_tiered_hash",2,7460972931250716399,"tiered","tiered",64,65,4096,8192,8192,16384,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",64,"tiered",64,"tiered",65,"tiered",64,31092593.74417014,44316419.233325951,55303616.856542416,1814882.0326678767,3157562.3618566468,622556,2608275452360560399
"baseline",4096,"adaptive_hash",2,7460972931250716399,"vector","tiered",64,65,4096,8192,8192,16384,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",64,"vector",64,"vector->tiered",65,"vector",64,32599837.000814997,578368999.42163098,76359193.646915093,1870207.5930428277,434782.60869565216,622556,2608275452360560399
"baseline",4096,"forced_tiered_hash",3,15361207867526911576,"tiered","tiered",64,65,4096,8192,8192,16384,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",64,"tiered",64,"tiered",65,"tiered",64,32899065.666535072,45599635.202918373,56201877.142696567,1756543.1231336729,3152585.1197982342,625088,2608821061764209734
"baseline",4096,"adaptive_hash",3,15361207867526911576,"vector","tiered",64,65,4096,8192,8192,16384,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",64,"vector",64,"vector->tiered",65,"vector",64,33017466.239640769,583771161.70461178,76787222.606158346,1815870.7100054477,413018.33801420784,625088,2608821061764209734
"baseline",4096,"forced_vector_hash",3,15361207867526911576,"vector","vector",64,64,4096,8192,8192,16384,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",64,"vector",64,"vector",64,"vector",64,16115516.018822923,646412411.11829352,74744001.79385604,313952.02813010174,418988.56161226804,623136,2608821061764209734
"baseline",4096,"adaptive_hash",4,11903448878383901159,"vector","tiered",64,65,4096,8192,8192,16384,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",64,"vector",64,"vector->tiered",65,"vector",64,32381322.453208987,589970501.47492623,76740081.344486222,1803101.3342949871,393638.79703983624,627620,2609006760121496943
"baseline",4096,"forced_vector_hash",4,11903448878383901159,"vector","vector",64,64,4096,8192,8192,16384,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",64,"vector",64,"vector",64,"vector",64,16684463.427656166,668449197.86096251,76446754.835257247,313342.10691232688,418095.15845806507,623136,2609006760121496943
"baseline",4096,"forced_tiered_hash",4,11903448878383901159,"tiered","tiered",64,65,4096,8192,8192,16384,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",64,"tiered",64,"tiered",65,"tiered",64,31838008.214206122,44480028.46721822,55318913.536538146,1772421.1272598368,3093102.3816888337,627620,2609006760121496943
"baseline",4096,"forced_tiered_hash",5,14750161124092376284,"tiered","tiered",64,65,4096,8192,8192,16384,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",64,"tiered",64,"tiered",65,"tiered",64,33208248.929033969,45722646.42677518,54761513.608236134,1883239.1713747645,3080714.7258163895,622556,2606581900541539285
"baseline",4096,"adaptive_hash",5,14750161124092376284,"vector","tiered",64,65,4096,8192,8192,16384,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",64,"vector",64,"vector->tiered",65,"vector",64,33283408.221001834,574712643.67816091,76698880.196349144,1798561.1510791369,422047.77580822154,622556,2606581900541539285
"baseline",4096,"forced_vector_hash",5,14750161124092376284,"vector","vector",64,64,4096,8192,8192,16384,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",64,"vector",64,"vector",64,"vector",64,17349966.167565972,660938532.71645737,78027465.667915106,297026.76211126626,421247.73579342011,623136,2606581900541539285
"baseline",4096,"adaptive_hash",6,5292099999092531180,"vector","tiered",64,65,4096,8192,8192,16384,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",64,"vector",64,"vector->tiered",65,"vector",64,32252862.441541687,590318772.13695395,78740157.480314955,1836884.6436443792,420062.16920104175,626776,2608914160727435722
"baseline",4096,"forced_vector_hash",6,5292099999092531180,"vector","vector",64,64,4096,8192,8192,16384,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",64,"vector",64,"vector",64,"vector",64,17002176.278563656,665778961.38482022,78486774.97841613,317783.14478200075,432656.94630727294,623136,2608914160727435722
"baseline",4096,"forced_tiered_hash",6,5292099999092531180,"tiered","tiered",64,65,4096,8192,8192,16384,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",64,"tiered",64,"tiered",65,"tiered",64,32526671.870934166,45460744.646997318,55694792.536897801,1793078.7161556394,3138731.9522912744,626776,2608914160727435722
"baseline",8192,"forced_tiered_hash",0,1533354404713909492,"tiered","tiered",91,91,8192,16384,16384,32768,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",91,"tiered",91,"tiered",91,"tiered",91,27054081.108135164,42929509.744998708,51714330.04085432,1309757.6948264572,2308402.5854108958,1237280,2600442685266303989
"baseline",8192,"adaptive_hash",0,1533354404713909492,"tiered","tiered",65,91,8192,16384,16384,32768,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",65,"tiered",65,"tiered",91,"tiered",65,29148570.262628619,40794680.373679273,49875311.72069826,1308729.2239235702,2704895.861509332,1237280,2600442685266303989
"baseline",8192,"forced_vector_hash",0,1533354404713909492,"vector","vector",91,91,8192,16384,16384,32768,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",91,"vector",91,"vector",91,"vector",91,8709511.6576813553,664893617.02127659,71813285.457809687,166747.26117623519,198736.03879327475,1245728,2600442685266303989
"baseline",8192,"adaptive_hash",1,8851810353577849580,"tiered","tiered",65,91,8192,16384,16384,32768,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",65,"tiered",65,"tiered",91,"tiered",65,28164253.928913422,40543279.951348066,50296750.82989639,1333688.9837289944,2703433.3603676669,1238436,2610692047236465301
"baseline",8192,"forced_vector_hash",1,8851810353577849580,"vector","vector",91,91,8192,16384,16384,32768,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",91,"vector",91,"vector",91,"vector",91,8783255.601521261,659630606.86015832,72385088.671733633,161540.44972861203,193757.14479471432,1245728,2610692047236465301
"baseline",8192,"forced_tiered_hash",1,8851810353577849580,"tiered","tiered",91,91,8192,16384,16384,32768,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",91,"tiered",91,"tiered",91,"tiered",91,27512587.008556414,43410314.290675469,51245259.813467249,1348435.8144552319,2285714.2857142854,1238436,2610692047236465301
"baseline",8192,"forced_vector_hash",2,6074101987220463306,"vector","vector",91,91,8192,16384,16384,32768,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",91,"vector",91,"vector",91,"vector",91,9024863.4989395794,654022236.75604963,71428571.428571433,163076.27077184001,199203.18725099601,1245728,2604400936210790827
"baseline",8192,"forced_tiered_hash",2,6074101987220463306,"tiered","tiered",91,91,8192,16384,16384,32768,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",91,"tiered",91,"tiered",91,"tiered",91,28787747.934479084,42892682.508364074,51116904.360271946,1256597.1349585322,2222222.2222222225,1238436,2604400936210790827
"baseline",8192,"adaptive_hash",2,6074101987220463306,"tiered","tiered",65,91,8192,16384,16384,32768,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",65,"tiered",65,"tiered",91,"tiered",65,28477047.499715228,40498947.027377293,50263885.398341291,1406074.24071991,2700513.097488523,1238436,2604400936210790827
"baseline",8192,"forced_vector_hash",3,2489097639920090271,"vector","vector",91,91,8192,16384,16384,32768,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",91,"vector",91,"vector",91,"vector",91,8906543.6376105528,637348629.70044613,71813285.457809687,172161.48747525178,192267.02043798426,1245728,2603956742105246981
"baseline",8192,"forced_tiered_hash",3,2489097639920090271,"tiered","tiered",91,91,8192,16384,16384,32768,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",91,"tiered",91,"tiered",91,"tiered",91,28417971.525192529,42078687.144961074,49578582.052553296,1286504.5670912133,2255808.7074216106,1237280,2603956742105246981
"baseline",8192,"adaptive_hash",3,2489097639920090271,"tiered","tiered",65,91,8192,16384,16384,32768,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",65,"tiered",65,"tiered",91,"tiered",65,28468129.928544994,36736343.264391467,47705371.624844961,1366493.5774801858,2704895.861509332,1237280,2603956742105246981
"baseline",8192,"adaptive_hash",4,1885911782505072956,"tiered","tiered",65,91,8192,16384,16384,32768,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",65,"tiered",65,"tiered",91,"tiered",65,28603300.820914734,40546567.733041398,50459178.524573617,1348617.6668914363,2722570.1061802343,1237280,2604212537617782366
"baseline",8192,"forced_vector_hash",4,1885911782505072956,"vector","vector",91,91,8192,16384,16384,32768,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",91,"vector",91,"vector",91,"vector",91,8824956.9783347305,657894736.84210527,72663856.997529432,164395.27199197753,200952.51492072421,1245728,2604212537617782366
"baseline",8192,"forced_tiered_hash",4,1885911782505072956,"tiered","tiered",91,91,8192,16384,16384,32768,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",91,"tiered",91,"tiered",91,"tiered",91,28479480.534275055,43012602.692588933,50306871.918704093,1317002.5023047545,2276867.0309653915,1237280,2604212537617782366
"baseline",8192,"forced_tiered_hash",5,2737113097762816153,"tiered","tiered",91,91,8192,16384,16384,32768,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",91,"tiered",91,"tiered",91,"tiered",91,28498147.620404676,42098172.939294435,50671395.996959716,1381597.1262779774,2319109.4619666049,1235716,2608173366147803176
"baseline",8192,"adaptive_hash",5,2737113097762816153,"tiered","tiered",65,91,8192,16384,16384,32768,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",65,"tiered",65,"tiered",91,"tiered",65,28656579.550664835,41177681.696520485,50357538.523516975,1332267.5193178789,2717391.3043478262,1235716,2608173366147803176
"baseline",8192,"forced_vector_hash",5,2737113097762816153,"vector","vector",91,91,8192,16384,16384,32768,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",91,"vector",91,"vector",91,"vector",91,8791517.9434881229,650618087.18282366,75063804.23359856,167504.18760469012,200835.47557840619,1245728,2608173366147803176
"baseline",8192,"forced_vector_hash",6,16149500638224588516,"vector","vector",91,91,8192,16384,16384,32768,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",91,"vector",91,"vector",91,"vector",91,8634907.4769663848,666222518.32111931,75035641.92991671,161269.51361114695,190901.62839089017,1245728,2604522381130608740
"baseline",8192,"forced_tiered_hash",6,16149500638224588516,"tiered","tiered",91,91,8192,16384,16384,32768,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",91,"tiered",91,"tiered",91,"tiered",91,29339279.427297268,42990413.137870252,52446635.548329569,1346982.7586206896,2207018.3182520415,1235716,2604522381130608740
"baseline",8192,"adaptive_hash",6,16149500638224588516,"tiered","tiered",65,91,8192,16384,16384,32768,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",65,"tiered",65,"tiered",91,"tiered",65,28920959.019001067,41523066.063198105,51002193.094303049,1426533.5235378032,2758620.6896551726,1235716,2604522381130608740
"baseline",16384,"forced_vector_hash",0,16519628863117178580,"vector","vector",128,128,16384,32768,32768,65536,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",128,"vector",128,"vector",128,"vector",128,4489579.6855498385,653594771.24182999,78889239.50773114,83519.163472058674,96723.959492005772,2490912,2811432233702823938
"baseline",16384,"forced_tiered_hash",0,16519628863117178580,"tiered","tiered",128,129,16384,32768,32768,65536,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",128,"tiered",128,"tiered",129,"tiered",128,23466466.419486552,40734856.816978291,44122837.980938934,923361.03416435828,1396453.0093562352,2465412,2811432233702823938
"baseline",16384,"adaptive_hash",0,16519628863117178580,"tiered","tiered",91,129,16384,32768,32768,65536,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",91,"tiered",91,"tiered",129,"tiered",91,22854008.593107231,38134462.113411888,50867287.247571088,868357.06842653698,2057189.8786257971,2465412,2811432233702823938
"baseline",16384,"adaptive_hash",1,12150548782170805316,"tiered","tiered",91,129,16384,32768,32768,65536,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",91,"tiered",91,"tiered",129,"tiered",91,23044660.552150067,35303254.960107327,51464155.215892136,836400.13382402144,2046245.1401677921,2465412,2809601574756132900
"baseline",16384,"forced_vector_hash",1,12150548782170805316,"vector","vector",128,128,16384,32768,32768,65536,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",128,"vector",128,"vector",128,"vector",128,4505356.8693176182,649350649.35064936,79157761.418507084,87278.313084764697,96229.71958659713,2490912,2809601574756132900
"baseline",16384,"forced_tiered_hash",1,12150548782170805316,"tiered","tiered",128,129,16384,32768,32768,65536,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",128,"tiered",128,"tiered",129,"tiered",128,22649543.611696225,41621576.625322565,50446451.092165664,837871.80561374116,1707941.9299743809,2465412,2809601574756132900
"baseline",16384,"adaptive_hash",2,14057633980897130801,"tiered","tiered",91,129,16384,32768,32768,65536,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",91,"tiered",91,"tiered",129,"tiered",91,23025028.205659553,38423115.346192271,50658561.296859168,866776.45835139125,2076843.1983385254,2465412,2849474804600164814
"baseline",16384,"forced_vector_hash",2,14057633980897130801,"vector","vector",128,128,16384,32768,32768,65536,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",128,"vector",128,"vector",128,"vector",128,4469873.0556052206,655307994.75753605,79955225.073958591,86878.708634874856,95758.840934989319,2490912,2849474804600164814
"baseline",16384,"forced_tiered_hash",2,14057633980897130801,"tiered","tiered",128,129,16384,32768,32768,65536,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",128,"tiered",128,"tiered",129,"tiered",128,23206163.557040751,40086587.027980439,51802735.184417732,975704.94682408043,1707358.7160662457,2465412,2849474804600164814
"baseline",16384,"forced_tiered_hash",3,9496396460044610260,"tiered","tiered",128,129,16384,32768,32768,65536,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",128,"tiered",128,"tiered",129,"tiered",128,23691629.747210313,40364898.684104308,51284681.265705928,956205.77548288403,1713796.0582690658,2465412,2809604592862375281
"baseline",16384,"adaptive_hash",3,9496396460044610260,"tiered","tiered",91,129,16384,32768,32768,65536,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",91,"tiered",91,"tiered",129,"tiered",91,23141719.892622419,38822889.975929804,44738725.841088042,835073.06889352819,2125850.3401360544,2465412,2809604592862375281
"baseline",16384,"forced_vector_hash",3,9496396460044610260,"vector","vector",128,128,16384,32768,32768,65536,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",128,"vector",128,"vector",128,"vector",128,4597257.2763089538,633312222.92590249,79674926.300693169,87185.4783867199,96414.350311900431,2490912,2809604592862375281
"baseline",16384,"adaptive_hash",4,14635596384326246855,"tiered","tiered",91,129,16384,32768,32768,65536,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",91,"tiered",91,"tiered",129,"tiered",91,22955260.197874345,38213152.967251323,37296732.806206174,810964.2364771713,2092925.910422771,2465412,2802782570264501827
"baseline",16384,"forced_vector_hash",4,14635596384326246855,"vector","vector",128,128,16384,32768,32768,65536,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",128,"vector",128,"vector",128,"vector",128,4496180.4946697783,651465798.04560268,79993600.511959031,84569.456894947827,94758.886014536023,2490912,2802782570264501827
"baseline",16384,"forced_tiered_hash",4,14635596384326246855,"tiered","tiered",128,129,16384,32768,32768,65536,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",128,"tiered",128,"tiered",129,"tiered",128,23009134.626446698,41142104.830083102,51551706.361480564,936241.92491339764,1738828.0299078419,2465412,2802782570264501827
"baseline",16384,"adaptive_hash",5,2683868946381181637,"tiered","tiered",91,129,16384,32768,32768,65536,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",91,"tiered",91,"tiered",129,"tiered",91,22778005.557833355,38630920.188518889,49967521.11127767,810832.72520878934,2072968.4908789385,2465412,2805449818878573377
"baseline",16384,"forced_vector_hash",5,2683868946381181637,"vector","vector",128,128,16384,32768,32768,65536,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",128,"vector",128,"vector",128,"vector",128,4443634.7154518515,649350649.35064936,79478620.251152441,87065.542940725776,98948.180837695298,2490912,2805449818878573377
"baseline",16384,"forced_tiered_hash",5,2683868946381181637,"tiered","tiered",128,129,16384,32768,32768,65536,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",128,"tiered",128,"tiered",129,"tiered",128,23255813.953488372,36956280.719908349,50045040.536482841,935628.74251497013,1709693.9647803043,2465412,2805449818878573377
"baseline",16384,"forced_tiered_hash",6,7266316803874473535,"tiered","tiered",128,129,16384,32768,32768,65536,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",128,"tiered",128,"tiered",129,"tiered",128,23616654.464728527,40329085.33634457,51602249.858093813,929108.98448387987,1710863.9863130883,2465412,2802923093430382200
"baseline",16384,"adaptive_hash",6,7266316803874473535,"tiered","tiered",91,129,16384,32768,32768,65536,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",91,"tiered",91,"tiered",129,"tiered",91,22878059.940517046,38170852.736850142,51559680.329981953,837100.28461409674,2079434.3938448743,2465412,2802923093430382200
"baseline",16384,"forced_vector_hash",6,7266316803874473535,"vector","vector",128,128,16384,32768,32768,65536,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",128,"vector",128,"vector",128,"vector",128,4471492.0027365535,641436818.47338045,77754451.442345068,86972.403656319846,94894.666919719108,2490912,2802923093430382200
"baseline",32768,"forced_tiered_hash",0,3799899914257613626,"tiered","tiered",182,182,32768,65536,65536,131072,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",182,"tiered",182,"tiered",182,"tiered",182,18081547.780490011,37212071.59602575,50994390.617032126,573000.22920009168,1245640.2590931738,4921232,5921195651255336034
"baseline",32768,"adaptive_hash",0,3799899914257613626,"tiered","tiered",129,182,32768,65536,65536,131072,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",129,"tiered",129,"tiered",182,"tiered",129,18079586.339064565,35783296.357260428,48744820.862783328,509658.01946893631,1541544.6277169725,4921232,5921195651255336034
"baseline",32768,"forced_vector_hash",0,3799899914257613626,"vector","vector",182,182,32768,65536,65536,131072,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",182,"vector",182,"vector",182,"vector",182,2246075.5445048637,637348629.70044613,80638658.172728002,43353.102348004024,48926.55146094683,4981280,5921195651255336034
"baseline",32768,"forced_vector_hash",1,14870762844571170300,"vector","vector",182,182,32768,65536,65536,131072,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",182,"vector",182,"vector",182,"vector",182,2274650.3862356353,614250614.25061429,68898994.074686512,43827.547366621817,47624.263609823931,4981280,5903990191892135316
"baseline",32768,"forced_tiered_hash",1,14870762844571170300,"tiered","tiered",182,182,32768,65536,65536,131072,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",182,"tiered",182,"tiered",182,"tiered",182,18031338.46625435,37676135.935498461,50994390.617032126,557631.18273573858,1253918.4952978056,4921232,5903990191892135316
"baseline",32768,"adaptive_hash",1,14870762844571170300,"tiered","tiered",129,182,32768,65536,65536,131072,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",129,"tiered",129,"tiered",182,"tiered",129,16707321.148127109,35651894.898213841,49058084.772370487,519076.04464053985,1548227.2797646695,4921232,5903990191892135316
"baseline",32768,"adaptive_hash",2,3668356801526459156,"tiered","tiered",129,182,32768,65536,65536,131072,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",129,"tiered",129,"tiered",182,"tiered",129,17825629.690368813,35951824.555096172,49748768.717974231,514165.2527122217,1523693.4328813043,4921232,5867651332793912201
"baseline",32768,"forced_vector_hash",2,3668356801526459156,"vector","vector",182,182,32768,65536,65536,131072,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",182,"vector",182,"vector",182,"vector",182,2285824.0052665384,610873549.17532074,81254570.569594547,43017.968605486516,47917.734832838978,4981280,5867651332793912201
"baseline",32768,"forced_tiered_hash",2,3668356801526459156,"tiered","tiered",182,182,32768,65536,65536,131072,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",182,"tiered",182,"tiered",182,"tiered",182,18491124.260355029,38431975.403535739,50266411.983512618,575373.99309551215,1244400.1991040318,4921232,5867651332793912201
"baseline",32768,"forced_vector_hash",3,5597019136430372439,"vector","vector",182,182,32768,65536,65536,131072,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",182,"vector",182,"vector",182,"vector",182,2267244.6629060637,600240096.03841531,79687624.5119133,44299.339496848108,47770.548501437894,4981280,5952659854051489931
"baseline",32768,"forced_tiered_hash",3,5597019136430372439,"tiered","tiered",182,182,32768,65536,65536,131072,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",182,"tiered",182,"tiered",182,"tiered",182,18790281.666322179,37717346.207520835,49287791.414066732,571004.3967338549,1260080.6451612904,4921232,5952659854051489931
"baseline",32768,"adaptive_hash",3,5597019136430372439,"tiered","tiered",129,182,32768,65536,65536,131072,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",129,"tiered",129,"tiered",182,"tiered",129,18053800.324968409,33225902.913911685,48612123.863691606,489620.05483744608,1558360.6046439146,4921232,5952659854051489931
"baseline",32768,"adaptive_hash",4,6810547043850850325,"tiered","tiered",129,182,32768,65536,65536,131072,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",129,"tiered",129,"tiered",182,"tiered",129,18033289.452328999,36264732.54759746,50125313.283208027,463306.15270570794,1557632.398753894,4921232,5886124142016643594
"baseline",32768,"forced_vector_hash",4,6810547043850850325,"vector","vector",182,182,32768,65536,65536,131072,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",182,"vector",182,"vector",182,"vector",182,2236391.0016571656,609756097.56097555,68376068.376068369,42827.286120533114,48879.199945255292,4981280,5886124142016643594
"baseline",32768,"forced_tiered_hash",4,6810547043850850325,"tiered","tiered",182,182,32768,65536,65536,131072,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",182,"tiered",182,"tiered",182,"tiered",182,18414849.734826162,36848699.240916796,50733093.196692206,578435.90930124943,1232134.056185313,4921232,5886124142016643594
"baseline",32768,"adaptive_hash",5,11482174959549226004,"tiered","tiered",129,182,32768,65536,65536,131072,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",129,"tiered",129,"tiered",182,"tiered",129,17838667.094794676,36209581.055147186,49838026.414154001,525762.35541535227,1533036.9461904033,4921232,5854156252755476422
"baseline",32768,"forced_vector_hash",5,11482174959549226004,"vector","vector",182,182,32768,65536,65536,131072,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",182,"vector",182,"vector",182,"vector",182,2227990.2414027425,589275191.5144372,77567483.710828424,42526.770602094017,47344.673960902765,4981280,5854156252755476422
"baseline",32768,"forced_tiered_hash",5,11482174959549226004,"tiered","tiered",182,182,32768,65536,65536,131072,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",182,"tiered",182,"tiered",182,"tiered",182,18402988.645356007,33594248.664628617,49394912.32403063,519750.51975051977,1255492.7809165097,4921232,5854156252755476422
"baseline",32768,"adaptive_hash",6,4266179595843056063,"tiered","tiered",129,182,32768,65536,65536,131072,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",129,"tiered",129,"tiered",182,"tiered",129,17943012.990741406,35944071.025484346,48683121.56175454,534702.17089081381,1548227.2797646695,4921232,5945944892063186673
"baseline",32768,"forced_vector_hash",6,4266179595843056063,"vector","vector",182,182,32768,65536,65536,131072,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",182,"vector",182,"vector",182,"vector",182,2226794.573746983,617283950.61728394,81806282.722513095,42699.459851832878,47783.787916435707,4981280,5945944892063186673
"baseline",32768,"forced_tiered_hash",6,4266179595843056063,"tiered","tiered",182,182,32768,65536,65536,131072,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",182,"tiered",182,"tiered",182,"tiered",182,18222238.419767484,37572797.294758596,49202912.812438495,552852.72003538255,1242081.7289777668,4921232,5945944892063186673
"baseline",65536,"forced_tiered_hash",0,10661120783772612247,"tiered","tiered",256,257,65536,131072,131072,262144,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",256,"tiered",256,"tiered",257,"tiered",256,13402849.445792176,34753596.997289218,45568466.621098205,329261.46653057192,899523.25267608173,9837616,7078339330582418198
"baseline",65536,"adaptive_hash",0,10661120783772612247,"tiered","tiered",182,257,65536,131072,131072,262144,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",182,"tiered",182,"tiered",257,"tiered",182,13422458.457491074,32863386.900653984,44482007.028157115,300237.18737802864,1125365.7438667566,9837616,7078339330582418198
"baseline",65536,"forced_vector_hash",0,10661120783772612247,"vector","vector",256,256,65536,131072,131072,262144,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",256,"vector",256,"vector",256,"vector",256,1119194.1801902631,498256103.63726956,62523446.292359635,21548.705461519399,23725.130132338778,9962016,7078339330582418198
"baseline",65536,"forced_vector_hash",1,3645920298421955121,"vector","vector",256,256,65536,131072,131072,262144,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",256,"vector",256,"vector",256,"vector",256,1040478.7867184964,374391613.62785476,44754744.002864301,21149.518636955821,22512.027050451703,9962016,7058462025128772180
"baseline",65536,"forced_tiered_hash",1,3645920298421955121,"tiered","tiered",256,257,65536,131072,131072,262144,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",256,"tiered",256,"tiered",257,"tiered",256,10046818.17268471,27078989.412115142,41592147.402570397,285103.34996436205,820209.97375328094,9837616,7058462025128772180
"baseline",65536,"adaptive_hash",1,3645920298421955121,"tiered","tiered",182,257,65536,131072,131072,262144,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",182,"tiered",182,"tiered",257,"tiered",182,11310937.676733401,31331265.469812326,43438599.539550841,283157.77551251557,1129177.9584462512,9837616,7058462025128772180
"baseline",65536,"forced_vector_hash",2,13440076570755554700,"vector","vector",256,256,65536,131072,131072,262144,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",256,"vector",256,"vector",256,"vector",256,1047528.4613482949,359582883.85472852,65638332.786347233,21566.969754481619,23456.668496286809,9962016,7031035215144323638
"baseline",65536,"forced_tiered_hash",2,13440076570755554700,"tiered","tiered",256,257,65536,131072,131072,262144,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",256,"tiered",256,"tiered",257,"tiered",256,13332800.02133248,34330049.091970205,45493835.585278191,315139.29156687256,912908.52656563802,9837616,7031035215144323638
"baseline",65536,"adaptive_hash",2,13440076570755554700,"tiered","tiered",182,257,65536,131072,131072,262144,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",182,"tiered",182,"tiered",257,"tiered",182,12777429.947740313,25755273.392227057,22394911.876021765,282757.45065882488,1079447.322970639,9837616,7031035215144323638
"baseline",65536,"forced_tiered_hash",3,265097094473494960,"tiered","tiered",256,257,65536,131072,131072,262144,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",256,"tiered",256,"tiered",257,"tiered",256,11666841.669291705,27756960.057734478,32832096.657692559,287811.19585551874,881057.26872246701,9837616,7048178356920180496
"baseline",65536,"adaptive_hash",3,265097094473494960,"tiered","tiered",182,257,65536,131072,131072,262144,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",182,"tiered",182,"tiered",257,"tiered",182,9837387.9767444152,30628809.458176363,43387712.599791735,299976.0019198464,1097815.3474585575,9837616,7048178356920180496
"baseline",65536,"forced_vector_hash",3,265097094473494960,"vector","vector",256,256,65536,131072,131072,262144,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",256,"vector",256,"vector",256,"vector",256,1099008.5843560523,490436488.47474253,71916576.770945698,22126.341409447949,22096.761719569997,9962016,7048178356920180496
"baseline",65536,"forced_tiered_hash",4,10511978180227547512,"tiered","tiered",256,257,65536,131072,131072,262144,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",256,"tiered",256,"tiered",257,"tiered",256,12827748.986607831,33056758.454265974,44620945.071616612,324002.07361327112,912492.0156948627,9837616,7047216827085719657
"baseline",65536,"adaptive_hash",4,10511978180227547512,"tiered","tiered",182,257,65536,131072,131072,262144,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",182,"tiered",182,"tiered",257,"tiered",182,11165449.632656708,24729826.643915229,42163848.716110803,281761.57335662562,1116819.2986374805,9837616,7047216827085719657
"baseline",65536,"forced_vector_hash",4,10511978180227547512,"vector","vector",256,256,65536,131072,131072,262144,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",256,"vector",256,"vector",256,"vector",256,1096373.4160145072,491159135.55992144,60727515.637335271,21258.096677572074,22895.240537397087,9962016,7047216827085719657
"baseline",65536,"adaptive_hash",5,18171218181464882810,"tiered","tiered",182,257,65536,131072,131072,262144,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",182,"tiered",182,"tiered",257,"tiered",182,13292216.078264568,31373533.287318818,38030043.734550297,295255.24816203606,986193.29388560168,9837616,6972502824690047116
"baseline",65536,"forced_vector_hash",5,18171218181464882810,"vector","vector",256,256,65536,131072,131072,262144,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",256,"vector",256,"vector",256,"vector",256,1075244.5374889383,475285171.10266161,63678043.81049414,21506.810131428116,22458.535928042853,9962016,6972502824690047116
"baseline",65536,"forced_tiered_hash",5,18171218181464882810,"tiered","tiered",256,257,65536,131072,131072,262144,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",256,"tiered",256,"tiered",257,"tiered",256,12614158.131086331,32799790.081343479,45798030.684680559,312460.94238220225,910166.56048056798,9837616,6972502824690047116
"baseline",65536,"forced_vector_hash",6,13709039232440699940,"vector","vector",256,256,65536,131072,131072,262144,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"vector",256,"vector",256,"vector",256,"vector",256,1089099.2060466788,501253132.83208019,71169311.792754963,20818.283449256476,22991.835599178728,9962016,7010486178119326697
"baseline",65536,"forced_tiered_hash",6,13709039232440699940,"tiered","tiered",256,257,65536,131072,131072,262144,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",256,"tiered",256,"tiered",257,"tiered",256,10754075.794726202,31665611.146295123,44218439.089100152,315666.52987783705,919793.96615158208,9837616,7010486178119326697
"baseline",65536,"adaptive_hash",6,13709039232440699940,"tiered","tiered",182,257,65536,131072,131072,262144,100000,97000,1000,500,500,1000,1000,100000,100000,1000,1000,"tiered",182,"tiered",182,"tiered",257,"tiered",182,13104614.134636804,29824038.174768861,42513391.718391292,311342.19620785204,1135331.5168029063,9837616,7010486178119326697
1 profile n container repeat seed initial_mode mixed_mode initial_leaf mixed_leaf initial_logical_capacity logical_capacity initial_hash_buckets hash_buckets operations read_count find_count find_hit_count find_miss_count insert_count erase_count read_batch_count find_batch_count insert_batch_count erase_batch_count read_mode read_leaf find_mode find_leaf insert_mode insert_leaf erase_mode erase_leaf mixed_ops_per_sec read_ops_per_sec find_ops_per_sec insert_ops_per_sec erase_ops_per_sec bytes checksum
2 baseline 256 forced_vector_hash 0 11643429248495452671 vector vector 16 16 256 512 512 1024 100000 97000 1000 500 500 1000 1000 100000 100000 1000 256 vector 16 vector 16 vector 16 vector 16 167168171.18020728 654450261.78010476 148367952.52225518 1816200.5085361423 8101265.8227848094 39456 2516218684603091152
3 baseline 256 forced_tiered_hash 0 11643429248495452671 tiered tiered 16 17 256 512 512 1024 100000 97000 1000 500 500 1000 1000 100000 100000 1000 256 tiered 16 tiered 16 tiered 33 tiered 4 42591251.756889135 54531573.781219326 82209799.408089444 2222716.159146477 4620938.628158845 43752 2516218684603091152
4 baseline 256 adaptive_hash 0 11643429248495452671 vector vector 16 16 256 512 512 1024 100000 97000 1000 500 500 1000 1000 100000 100000 1000 256 vector 16 vector 16 vector 16 vector 16 158579130.98636219 583430571.76196039 147167034.58425313 1857010.2135561744 8258064.5161290318 39456 2516218684603091152
5 baseline 256 forced_vector_hash 1 15449987037075203961 vector vector 16 16 256 512 512 1024 100000 97000 1000 500 500 1000 1000 100000 100000 1000 256 vector 16 vector 16 vector 16 vector 16 150172698.60339391 637755102.04081643 143636885.95231256 1678697.330871244 8050314.4654088048 39456 2515907659441309718
6 baseline 256 forced_tiered_hash 1 15449987037075203961 tiered tiered 16 17 256 512 512 1024 100000 97000 1000 500 500 1000 1000 100000 100000 1000 256 tiered 16 tiered 16 tiered 33 tiered 4 41291601.288297959 52706477.62610025 80392314.494734302 2383790.2264600713 5192697.7687626779 44132 2515907659441309718
7 baseline 256 adaptive_hash 1 15449987037075203961 vector vector 16 16 256 512 512 1024 100000 97000 1000 500 500 1000 1000 100000 100000 1000 256 vector 16 vector 16 vector 16 vector 16 149320591.30954158 567536889.89784336 143533802.21042055 1709109.5539224066 8126984.1269841269 39456 2515907659441309718
8 baseline 256 forced_tiered_hash 2 18085065223655675095 tiered tiered 16 17 256 512 512 1024 100000 97000 1000 500 500 1000 1000 100000 100000 1000 256 tiered 16 tiered 16 tiered 33 tiered 4 39816842.524387822 53231129.564569362 78876794.447073683 2263980.0769753228 5203252.0325203249 43752 2516672311691009245
9 baseline 256 adaptive_hash 2 18085065223655675095 vector vector 16 16 256 512 512 1024 100000 97000 1000 500 500 1000 1000 100000 100000 1000 256 vector 16 vector 16 vector 16 vector 16 153186274.50980392 557103064.06685233 138715494.52073798 1654533.421575116 8619528.6195286196 39456 2516672311691009245
10 baseline 256 forced_vector_hash 2 18085065223655675095 vector vector 16 16 256 512 512 1024 100000 97000 1000 500 500 1000 1000 100000 100000 1000 256 vector 16 vector 16 vector 16 vector 16 169262017.60324982 660066006.60066009 143595634.69270533 1761804.0873854826 9045936.3957597166 39456 2516672311691009245
11 baseline 256 adaptive_hash 3 9610393720456262672 vector vector 16 16 256 512 512 1024 100000 97000 1000 500 500 1000 1000 100000 100000 1000 256 vector 16 vector 16 vector 16 vector 16 151676020.02123463 571755288.73642087 146584579.30225742 1740644.0382941687 9078014.1843971629 39456 2516336871995962384
12 baseline 256 forced_vector_hash 3 9610393720456262672 vector vector 16 16 256 512 512 1024 100000 97000 1000 500 500 1000 1000 100000 100000 1000 256 vector 16 vector 16 vector 16 vector 16 166002656.04249668 660501981.50594449 147623265.42663124 1699235.344095157 8476821.1920529809 39456 2516336871995962384
13 baseline 256 forced_tiered_hash 3 9610393720456262672 tiered tiered 16 17 256 512 512 1024 100000 97000 1000 500 500 1000 1000 100000 100000 1000 256 tiered 16 tiered 16 tiered 33 tiered 4 42925824.17582418 54182921.543129608 81479670.822129875 2552322.6135783563 5423728.8135593217 43920 2516336871995962384
14 baseline 256 forced_vector_hash 4 13992777055088439294 vector vector 16 16 256 512 512 1024 100000 97000 1000 500 500 1000 1000 100000 100000 1000 256 vector 16 vector 16 vector 16 vector 16 150693188.66787222 639795265.51503515 145369966.5649077 1670564.650851988 8205128.205128205 39456 2516698023620607488
15 baseline 256 forced_tiered_hash 4 13992777055088439294 tiered tiered 16 17 256 512 512 1024 100000 97000 1000 500 500 1000 1000 100000 100000 1000 256 tiered 16 tiered 16 tiered 33 tiered 4 39794659.556687489 53524594.551196277 81208380.704888746 2526528.5497726123 5192697.7687626779 44132 2516698023620607488
16 baseline 256 adaptive_hash 4 13992777055088439294 vector vector 16 16 256 512 512 1024 100000 97000 1000 500 500 1000 1000 100000 100000 1000 256 vector 16 vector 16 vector 16 vector 16 160000000 576701268.74279118 141944641.58977997 1837897.4453225508 8504983.3887043186 39456 2516698023620607488
17 baseline 256 forced_tiered_hash 5 5937878447685887746 tiered tiered 16 17 256 512 512 1024 100000 97000 1000 500 500 1000 1000 100000 100000 1000 256 tiered 16 tiered 16 tiered 33 tiered 4 42665756.463862106 55075177.617447816 83008217.813563541 2522068.0958385877 5481798.7152034268 43484 2516157113323081812
18 baseline 256 adaptive_hash 5 5937878447685887746 vector vector 16 16 256 512 512 1024 100000 97000 1000 500 500 1000 1000 100000 100000 1000 256 vector 16 vector 16 vector 16 vector 16 133886731.82487616 569800569.80056977 146649068.77841324 1596424.0102171137 8421052.6315789465 39456 2516157113323081812
19 baseline 256 forced_vector_hash 5 5937878447685887746 vector vector 16 16 256 512 512 1024 100000 97000 1000 500 500 1000 1000 100000 100000 1000 256 vector 16 vector 16 vector 16 vector 16 164500740.25333115 650618087.18282366 143430866.32243258 1739432.9448599755 8648648.6486486476 39456 2516157113323081812
20 baseline 256 forced_tiered_hash 6 11237391578505003154 tiered tiered 16 17 256 512 512 1024 100000 97000 1000 500 500 1000 1000 100000 100000 1000 256 tiered 16 tiered 16 tiered 33 tiered 4 40705010.786827855 54262303.977426879 83654007.026936576 2459419.5769798327 5481798.7152034268 44400 2516652954639355697
21 baseline 256 adaptive_hash 6 11237391578505003154 vector vector 16 16 256 512 512 1024 100000 97000 1000 500 500 1000 1000 100000 100000 1000 256 vector 16 vector 16 vector 16 vector 16 133440085.40165466 573723465.28973031 146134736.22680113 1777461.7845716316 7734138.9728096677 39456 2516652954639355697
22 baseline 256 forced_vector_hash 6 11237391578505003154 vector vector 16 16 256 512 512 1024 100000 97000 1000 500 500 1000 1000 100000 100000 1000 256 vector 16 vector 16 vector 16 vector 16 141302811.92595732 660938532.71645737 147492625.36873156 1755926.2510974538 7619047.6190476194 39456 2516652954639355697
23 baseline 512 forced_tiered_hash 0 14984970845100107740 tiered tiered 23 23 512 1024 1024 2048 100000 97000 1000 500 500 1000 1000 100000 100000 1000 512 tiered 23 tiered 23 tiered 33 tiered 4 39111389.236545682 50032521.138740182 78727759.407967255 2598077.4227071968 5289256.1983471075 84724 2516449983142523160
24 baseline 512 adaptive_hash 0 14984970845100107740 vector vector 23 23 512 1024 1024 2048 100000 97000 1000 500 500 1000 1000 100000 100000 1000 512 vector 23 vector 23 vector 23 vector 23 94822681.585435241 576701268.74279118 130225289.7512697 1323801.9592268995 5378151.260504202 78368 2516449983142523160
25 baseline 512 forced_vector_hash 0 14984970845100107740 vector vector 23 23 512 1024 1024 2048 100000 97000 1000 500 500 1000 1000 100000 100000 1000 512 vector 23 vector 23 vector 23 vector 23 98463962.189838529 651890482.39895701 130718954.24836603 1371930.3059404583 5251282.051282051 78368 2516449983142523160
26 baseline 512 adaptive_hash 1 16304051715571137494 vector vector 23 23 512 1024 1024 2048 100000 97000 1000 500 500 1000 1000 100000 100000 1000 512 vector 23 vector 23 vector 23 vector 23 98328416.912487715 567859170.92561042 129065565.30717605 1272264.631043257 5517241.3793103443 78368 2516668772023801606
27 baseline 512 forced_vector_hash 1 16304051715571137494 vector vector 23 23 512 1024 1024 2048 100000 97000 1000 500 500 1000 1000 100000 100000 1000 512 vector 23 vector 23 vector 23 vector 23 102301790.28132991 652741514.3603133 129584035.2468576 1286670.0977869274 4543034.6051464062 78368 2516668772023801606
28 baseline 512 forced_tiered_hash 1 16304051715571137494 tiered tiered 23 23 512 1024 1024 2048 100000 97000 1000 500 500 1000 1000 100000 100000 1000 512 tiered 23 tiered 23 tiered 33 tiered 4 37228695.878783368 49842994.567113593 79170295.305201486 2661698.163428267 5366876.3102725362 85064 2516668772023801606
29 baseline 512 forced_vector_hash 2 9563555999342315634 vector vector 23 23 512 1024 1024 2048 100000 97000 1000 500 500 1000 1000 100000 100000 1000 512 vector 23 vector 23 vector 23 vector 23 103412616.33919339 657894736.84210527 130907186.80455558 1358880.2826470986 5135406.2186559681 78368 2516540277992719419
30 baseline 512 forced_tiered_hash 2 9563555999342315634 tiered tiered 23 23 512 1024 1024 2048 100000 97000 1000 500 500 1000 1000 100000 100000 1000 512 tiered 23 tiered 23 tiered 33 tiered 4 38556446.637877852 50152966.547971316 78963992.419456735 2784739.62684489 5344467.6409185808 85404 2516540277992719419
31 baseline 512 adaptive_hash 2 9563555999342315634 vector vector 23 23 512 1024 1024 2048 100000 97000 1000 500 500 1000 1000 100000 100000 1000 512 vector 23 vector 23 vector 23 vector 23 94366330.093422666 575373993.09551215 130174433.74121322 1284026.7077555214 4361158.4327086881 78368 2516540277992719419
32 baseline 512 adaptive_hash 3 14934861932824753028 vector vector 23 23 512 1024 1024 2048 100000 97000 1000 500 500 1000 1000 100000 100000 1000 512 vector 23 vector 23 vector 23 vector 23 93870271.285084024 575705238.91767418 131061598.95150721 1336183.8588989845 5140562.248995984 78368 2516478738380764097
33 baseline 512 forced_vector_hash 3 14934861932824753028 vector vector 23 23 512 1024 1024 2048 100000 97000 1000 500 500 1000 1000 100000 100000 1000 512 vector 23 vector 23 vector 23 vector 23 101327388.79319081 650618087.18282366 127860887.35455824 1337613.6971642592 4530973.4513274338 78368 2516478738380764097
34 baseline 512 forced_tiered_hash 3 14934861932824753028 tiered tiered 23 23 512 1024 1024 2048 100000 97000 1000 500 500 1000 1000 100000 100000 1000 512 tiered 23 tiered 23 tiered 33 tiered 4 38090884.851255096 49756194.646233454 78143314.839415491 2698327.0372369131 5262076.0534429606 86592 2516478738380764097
35 baseline 512 adaptive_hash 4 7237444903719400313 vector vector 23 23 512 1024 1024 2048 100000 97000 1000 500 500 1000 1000 100000 100000 1000 512 vector 23 vector 23 vector 23 vector 23 105596620.90813093 577700751.01097631 126582278.48101266 1300221.0375763881 5256673.5112936348 78368 2516642161409729628
36 baseline 512 forced_vector_hash 4 7237444903719400313 vector vector 23 23 512 1024 1024 2048 100000 97000 1000 500 500 1000 1000 100000 100000 1000 512 vector 23 vector 23 vector 23 vector 23 107054919.17353603 657030223.39027584 125203455.615375 1225940.9096481549 5283797.7296181628 78368 2516642161409729628
37 baseline 512 forced_tiered_hash 4 7237444903719400313 tiered tiered 23 23 512 1024 1024 2048 100000 97000 1000 500 500 1000 1000 100000 100000 1000 512 tiered 23 tiered 23 tiered 33 tiered 4 38755183.505793899 50877639.277537525 78963992.419456735 2645502.6455026455 5355648.5355648529 86252 2516642161409729628
38 baseline 512 adaptive_hash 5 14701316826153139949 vector vector 23 23 512 1024 1024 2048 100000 97000 1000 500 500 1000 1000 100000 100000 1000 512 vector 23 vector 23 vector 23 vector 23 106405618.21664184 566893424.03628111 123823675.08667657 1296176.2799740764 5120000 78368 2516651054704929232
39 baseline 512 forced_vector_hash 5 14701316826153139949 vector vector 23 23 512 1024 1024 2048 100000 97000 1000 500 500 1000 1000 100000 100000 1000 512 vector 23 vector 23 vector 23 vector 23 115526802.2181146 664451827.24252486 121802679.65895249 1362397.8201634879 4172779.1361043192 78368 2516651054704929232
40 baseline 512 forced_tiered_hash 5 14701316826153139949 tiered tiered 23 23 512 1024 1024 2048 100000 97000 1000 500 500 1000 1000 100000 100000 1000 512 tiered 23 tiered 23 tiered 33 tiered 4 39354584.809130266 50597045.132564254 79333597.778659269 2932551.319648094 5372507.8698845748 85064 2516651054704929232
41 baseline 512 adaptive_hash 6 2747466510094964666 vector vector 23 23 512 1024 1024 2048 100000 97000 1000 500 500 1000 1000 100000 100000 1000 512 vector 23 vector 23 vector 23 vector 23 103616205.57455187 569151963.57427442 129098889.74954817 1428775.5393627661 5294725.9565667007 78368 2532477225510751123
42 baseline 512 forced_vector_hash 6 2747466510094964666 vector vector 23 23 512 1024 1024 2048 100000 97000 1000 500 500 1000 1000 100000 100000 1000 512 vector 23 vector 23 vector 23 vector 23 104569695.70218551 662251655.62913907 133511348.46461949 1441337.5612568464 5014691.4789422136 78368 2532477225510751123
43 baseline 512 forced_tiered_hash 6 2747466510094964666 tiered tiered 23 23 512 1024 1024 2048 100000 97000 1000 500 500 1000 1000 100000 100000 1000 512 tiered 23 tiered 23 tiered 33 tiered 4 39379380.956131369 49183553.019870147 79808459.696727857 2887669.6505919723 5084409.1360476669 85744 2532477225510751123
44 baseline 1024 adaptive_hash 0 8263605205929953231 vector vector 32 32 1024 2048 2048 4096 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 32 vector 32 vector 32 vector 32 61117222.833394453 584112149.53271031 114573785.51787351 934666.79128890554 2944640.7538280329 156192 2533248489645560685
45 baseline 1024 forced_vector_hash 0 8263605205929953231 vector vector 32 32 1024 2048 2048 4096 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 32 vector 32 vector 32 vector 32 59220656.164870307 666666666.66666675 113623451.88046813 946073.79375591292 2983293.5560859186 156192 2533248489645560685
46 baseline 1024 forced_tiered_hash 0 8263605205929953231 tiered tiered 32 33 1024 2048 2048 4096 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 32 tiered 32 tiered 33 tiered 6 35664610.007489569 45676700.31516923 70721357.85007073 3117206.9825436408 4653327.1288971612 164256 2533248489645560685
47 baseline 1024 forced_vector_hash 1 7082387021252559084 vector vector 32 32 1024 2048 2048 4096 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 32 vector 32 vector 32 vector 32 58816609.810610518 662251655.62913907 112854079.67498025 931619.15408980811 2919708.0291970805 156192 2533011276197967348
48 baseline 1024 forced_tiered_hash 1 7082387021252559084 tiered tiered 32 33 1024 2048 2048 4096 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 32 tiered 32 tiered 33 tiered 6 35463508.050216332 46358536.924574666 70982396.365701303 3224766.2044501775 4657661.8537494177 165176 2533011276197967348
49 baseline 1024 adaptive_hash 1 7082387021252559084 vector vector 32 32 1024 2048 2048 4096 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 32 vector 32 vector 32 vector 32 56631555.102503113 583771161.70461178 108695652.17391305 911743.2530999271 2893518.5185185187 156192 2533011276197967348
50 baseline 1024 forced_tiered_hash 2 9982579891442255758 tiered tiered 32 33 1024 2048 2048 4096 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 32 tiered 32 tiered 33 tiered 6 35683699.685983442 46957175.056348607 71352122.725651085 3155569.5803092457 4775549.1881566383 166772 2532625679719947189
51 baseline 1024 adaptive_hash 2 9982579891442255758 vector vector 32 32 1024 2048 2048 4096 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 32 vector 32 vector 32 vector 32 58997050.147492632 589622641.50943398 109673173.94165386 888730.89228581579 2796420.5816554809 156192 2532625679719947189
52 baseline 1024 forced_vector_hash 2 9982579891442255758 vector vector 32 32 1024 2048 2048 4096 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 32 vector 32 vector 32 vector 32 59648076.349537723 663129973.47480106 109349371.24111538 983284.1691248772 2395783.4211787255 156192 2532625679719947189
53 baseline 1024 forced_tiered_hash 3 6223662145476107803 tiered tiered 32 33 1024 2048 2048 4096 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 32 tiered 32 tiered 33 tiered 6 35713010.249633946 46853769.385747083 71397972.297586754 3168567.8073510774 4866180.0486618001 166096 2532988634027205437
54 baseline 1024 adaptive_hash 3 6223662145476107803 vector vector 32 32 1024 2048 2048 4096 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 32 vector 32 vector 32 vector 32 61591525.006159149 590667454.22327232 109613065.8774526 936154.27822505147 2962085.3080568719 156192 2532988634027205437
55 baseline 1024 forced_vector_hash 3 6223662145476107803 vector vector 32 32 1024 2048 2048 4096 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 32 vector 32 vector 32 vector 32 60081711.127132908 658327847.26793945 109051254.08942203 971439.67359626968 2790178.5714285714 156192 2532988634027205437
56 baseline 1024 adaptive_hash 4 7077184263008060768 vector vector 32 32 1024 2048 2048 4096 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 32 vector 32 vector 32 vector 32 51958848.591915205 584453535.94389248 113083795.09216329 926268.98851426458 2952465.308532625 156192 2533026764089274159
57 baseline 1024 forced_vector_hash 4 7077184263008060768 vector vector 32 32 1024 2048 2048 4096 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 32 vector 32 vector 32 vector 32 55922156.358349182 670241286.86327076 113430127.0417423 912741.87659729831 2801120.4481792715 156192 2533026764089274159
58 baseline 1024 forced_tiered_hash 4 7077184263008060768 tiered tiered 32 33 1024 2048 2048 4096 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 32 tiered 32 tiered 33 tiered 6 35472313.859033026 45400889.857441209 71751452.966922581 3144654.0880503147 4537205.081669692 164716 2533026764089274159
59 baseline 1024 forced_tiered_hash 5 3198208010137807867 tiered tiered 32 33 1024 2048 2048 4096 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 32 tiered 32 tiered 33 tiered 6 35835871.707579285 46172315.079878107 71118697.105469033 3102699.3484331369 4782400.7651841231 166096 2532977876410076447
60 baseline 1024 adaptive_hash 5 3198208010137807867 vector vector 32 32 1024 2048 2048 4096 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 32 vector 32 vector 32 vector 32 52623270.009998426 307219662.05837172 81426593.925576091 751145.49688274611 2209944.7513812156 156192 2532977876410076447
61 baseline 1024 forced_vector_hash 5 3198208010137807867 vector vector 32 32 1024 2048 2048 4096 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 32 vector 32 vector 32 vector 32 45793836.149654262 351988736.36043644 82994439.372562036 754147.81297134247 2220248.6678507994 156192 2532977876410076447
62 baseline 1024 adaptive_hash 6 878090231870985884 vector vector 32 32 1024 2048 2048 4096 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 32 vector 32 vector 32 vector 32 47361939.945060156 540248514.31658566 110168557.89357717 882145.37755822157 2691790.0403768509 156192 2533145598879769717
63 baseline 1024 forced_vector_hash 6 878090231870985884 vector vector 32 32 1024 2048 2048 4096 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 32 vector 32 vector 32 vector 32 57286892.758936755 656167979.00262475 110411836.14883517 918020.74726888817 2845759.8178713717 156192 2533145598879769717
64 baseline 1024 forced_tiered_hash 6 878090231870985884 tiered tiered 32 33 1024 2048 2048 4096 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 32 tiered 32 tiered 33 tiered 6 34379619.761405438 44738725.841088042 69895855.175788075 2955956.251847473 4748338.0816714149 166772 2533145598879769717
65 baseline 2048 forced_tiered_hash 0 17604282621400283443 tiered tiered 46 46 2048 4096 4096 8192 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 46 tiered 46 tiered 46 tiered 46 31505986.137366097 41753653.444676407 59154096.421177171 2295684.113865932 2778549.5971103082 321736 2555495986187765739
66 baseline 2048 adaptive_hash 0 17604282621400283443 vector vector 46 46 2048 4096 4096 8192 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 46 vector 46 vector 46 vector 46 28851702.250432778 572409845.44934177 78758761.912262738 567086.31053646363 960891.7075045642 311840 2555495986187765739
67 baseline 2048 forced_vector_hash 0 17604282621400283443 vector vector 46 46 2048 4096 4096 8192 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 46 vector 46 vector 46 vector 46 32079042.761364002 645161290.3225807 79107665.532790124 566829.15769187163 867077.0831526923 311840 2555495986187765739
68 baseline 2048 forced_tiered_hash 1 16081151116256482927 tiered tiered 46 46 2048 4096 4096 8192 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 46 tiered 46 tiered 46 tiered 46 32177102.773666259 43327556.325823218 58768218.147625767 2600104.0041601663 3813882.5324180019 322968 2556698868577027992
69 baseline 2048 adaptive_hash 1 16081151116256482927 vector vector 46 46 2048 4096 4096 8192 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 46 vector 46 vector 46 vector 46 31330283.852371704 580383052.81485784 81433224.755700335 568795.85916614521 995619.2751891677 311840 2556698868577027992
70 baseline 2048 forced_vector_hash 1 16081151116256482927 vector vector 46 46 2048 4096 4096 8192 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 46 vector 46 vector 46 vector 46 31755104.633069765 661813368.63004637 81333875.559170395 575175.42850569426 978377.84952548682 311840 2556698868577027992
71 baseline 2048 forced_vector_hash 2 18436629033319334241 vector vector 46 46 2048 4096 4096 8192 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 46 vector 46 vector 46 vector 46 31345014.575431775 651465798.04560268 82155767.334866896 570125.42759407067 973520.24922118383 311840 2556276914809095413
72 baseline 2048 forced_tiered_hash 2 18436629033319334241 tiered tiered 46 46 2048 4096 4096 8192 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 46 tiered 46 tiered 46 tiered 46 32539372.640895486 43639537.420903333 59559261.465157837 2566735.1129363449 3907776.4751856197 323584 2556276914809095413
73 baseline 2048 adaptive_hash 2 18436629033319334241 vector vector 46 46 2048 4096 4096 8192 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 46 vector 46 vector 46 vector 46 33111486.374623358 585823081.42940831 82290980.908492431 562651.21251336299 935016.36278634879 311840 2556276914809095413
74 baseline 2048 forced_vector_hash 3 5149219854132784233 vector vector 46 46 2048 4096 4096 8192 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 46 vector 46 vector 46 vector 46 29173230.643561468 654878847.41322851 80619155.111254439 546716.96462741238 956022.94455066917 311840 2556998120563425210
75 baseline 2048 forced_tiered_hash 3 5149219854132784233 tiered tiered 46 46 2048 4096 4096 8192 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 46 tiered 46 tiered 46 tiered 46 32399157.621901833 43691017.126878709 58210605.972408168 2511931.6754584275 3869969.0402476783 321736 2556998120563425210
76 baseline 2048 adaptive_hash 3 5149219854132784233 vector vector 46 46 2048 4096 4096 8192 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 46 vector 46 vector 46 vector 46 28376039.272438351 578703703.70370376 81406707.91273202 542652.48534838296 1007759.7500755819 311840 2556998120563425210
77 baseline 2048 adaptive_hash 4 11228077710321852230 vector vector 46 46 2048 4096 4096 8192 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 46 vector 46 vector 46 vector 46 30816640.98613251 569800569.80056977 80070462.006565779 579710.14492753625 1005732.6762546515 311840 2556450339210586434
78 baseline 2048 forced_vector_hash 4 11228077710321852230 vector vector 46 46 2048 4096 4096 8192 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 46 vector 46 vector 46 vector 46 33212660.666245975 663570006.63570011 81426593.925576091 596338.48172222555 973615.03261610365 311840 2556450339210586434
79 baseline 2048 forced_tiered_hash 4 11228077710321852230 tiered tiered 46 46 2048 4096 4096 8192 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 46 tiered 46 tiered 46 tiered 46 32150205.761316869 43252595.155709349 59157595.835305251 2615062.7615062762 3880481.1796662789 320608 2556450339210586434
80 baseline 2048 adaptive_hash 5 9566722756865290973 vector vector 46 46 2048 4096 4096 8192 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 46 vector 46 vector 46 vector 46 31993857.179421552 587199060.48150325 82088327.039894924 558628.0096084018 965344.14518775942 311840 2555871518819976418
81 baseline 2048 forced_vector_hash 5 9566722756865290973 vector vector 46 46 2048 4096 4096 8192 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 46 vector 46 vector 46 vector 46 32189531.964205239 669792364.36704624 83104795.146679968 547225.5663784612 975039.0015600624 311840 2555871518819976418
82 baseline 2048 forced_tiered_hash 5 9566722756865290973 tiered tiered 46 46 2048 4096 4096 8192 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 46 tiered 46 tiered 46 tiered 46 31830914.183855359 42920297.008455291 59783583.427990675 2710027.1002710024 3894080.9968847353 320608 2555871518819976418
83 baseline 2048 adaptive_hash 6 11775567059054511224 vector vector 46 46 2048 4096 4096 8192 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 46 vector 46 vector 46 vector 46 32073898.261594713 581395348.83720922 81162243.324405476 565834.88937927911 1044059.302568386 311840 2556823170109389014
84 baseline 2048 forced_vector_hash 6 11775567059054511224 vector vector 46 46 2048 4096 4096 8192 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 46 vector 46 vector 46 vector 46 31979533.098816756 653167864.14108419 82304526.748971194 576202.82339383464 1034768.2119205298 311840 2556823170109389014
85 baseline 2048 forced_tiered_hash 6 11775567059054511224 tiered tiered 46 46 2048 4096 4096 8192 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 46 tiered 46 tiered 46 tiered 46 33871896.487484336 43940592.31918446 59220656.164870307 2581311.3061435209 3938558.4875935409 321736 2556823170109389014
86 baseline 4096 forced_tiered_hash 0 7600465057368479647 tiered tiered 64 65 4096 8192 8192 16384 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 64 tiered 64 tiered 65 tiered 64 32578595.862518322 46262028.1273131 54824561.403508767 1773678.60943597 3109452.736318408 628464 2606749384601431537
87 baseline 4096 adaptive_hash 0 7600465057368479647 vector tiered 64 65 4096 8192 8192 16384 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 64 vector 64 vector->tiered 65 vector 64 32639206.21450486 581395348.83720922 76710647.437864378 1886080.7242549981 434839.32686872198 628464 2606749384601431537
88 baseline 4096 forced_vector_hash 0 7600465057368479647 vector vector 64 64 4096 8192 8192 16384 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 64 vector 64 vector 64 vector 64 17404016.847088307 661813368.63004637 77297673.340032458 289435.60057887121 427880.70685892773 623136 2606749384601431537
89 baseline 4096 forced_vector_hash 1 7524986396680695453 vector vector 64 64 4096 8192 8192 16384 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 64 vector 64 vector 64 vector 64 17641040.115725223 638569604.0868454 76341705.473700285 315537.04404897132 429239.81628535863 623136 2601740960506217425
90 baseline 4096 forced_tiered_hash 1 7524986396680695453 tiered tiered 64 65 4096 8192 8192 16384 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 64 tiered 64 tiered 65 tiered 64 32450674.974039461 45363817.818907641 54030689.431597143 1796622.3499820339 3147623.5442241104 624244 2601740960506217425
91 baseline 4096 adaptive_hash 1 7524986396680695453 vector tiered 64 65 4096 8192 8192 16384 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 64 vector 64 vector->tiered 65 vector 64 32852590.426755149 581057524.69494474 76359193.646915093 1822821.7280349983 426712.18263281416 624244 2601740960506217425
92 baseline 4096 forced_vector_hash 2 7460972931250716399 vector vector 64 64 4096 8192 8192 16384 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 64 vector 64 vector 64 vector 64 17199862.401100792 666222518.32111931 78864353.312302843 299697.30572122155 380531.98371323111 623136 2608275452360560399
93 baseline 4096 forced_tiered_hash 2 7460972931250716399 tiered tiered 64 65 4096 8192 8192 16384 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 64 tiered 64 tiered 65 tiered 64 31092593.74417014 44316419.233325951 55303616.856542416 1814882.0326678767 3157562.3618566468 622556 2608275452360560399
94 baseline 4096 adaptive_hash 2 7460972931250716399 vector tiered 64 65 4096 8192 8192 16384 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 64 vector 64 vector->tiered 65 vector 64 32599837.000814997 578368999.42163098 76359193.646915093 1870207.5930428277 434782.60869565216 622556 2608275452360560399
95 baseline 4096 forced_tiered_hash 3 15361207867526911576 tiered tiered 64 65 4096 8192 8192 16384 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 64 tiered 64 tiered 65 tiered 64 32899065.666535072 45599635.202918373 56201877.142696567 1756543.1231336729 3152585.1197982342 625088 2608821061764209734
96 baseline 4096 adaptive_hash 3 15361207867526911576 vector tiered 64 65 4096 8192 8192 16384 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 64 vector 64 vector->tiered 65 vector 64 33017466.239640769 583771161.70461178 76787222.606158346 1815870.7100054477 413018.33801420784 625088 2608821061764209734
97 baseline 4096 forced_vector_hash 3 15361207867526911576 vector vector 64 64 4096 8192 8192 16384 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 64 vector 64 vector 64 vector 64 16115516.018822923 646412411.11829352 74744001.79385604 313952.02813010174 418988.56161226804 623136 2608821061764209734
98 baseline 4096 adaptive_hash 4 11903448878383901159 vector tiered 64 65 4096 8192 8192 16384 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 64 vector 64 vector->tiered 65 vector 64 32381322.453208987 589970501.47492623 76740081.344486222 1803101.3342949871 393638.79703983624 627620 2609006760121496943
99 baseline 4096 forced_vector_hash 4 11903448878383901159 vector vector 64 64 4096 8192 8192 16384 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 64 vector 64 vector 64 vector 64 16684463.427656166 668449197.86096251 76446754.835257247 313342.10691232688 418095.15845806507 623136 2609006760121496943
100 baseline 4096 forced_tiered_hash 4 11903448878383901159 tiered tiered 64 65 4096 8192 8192 16384 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 64 tiered 64 tiered 65 tiered 64 31838008.214206122 44480028.46721822 55318913.536538146 1772421.1272598368 3093102.3816888337 627620 2609006760121496943
101 baseline 4096 forced_tiered_hash 5 14750161124092376284 tiered tiered 64 65 4096 8192 8192 16384 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 64 tiered 64 tiered 65 tiered 64 33208248.929033969 45722646.42677518 54761513.608236134 1883239.1713747645 3080714.7258163895 622556 2606581900541539285
102 baseline 4096 adaptive_hash 5 14750161124092376284 vector tiered 64 65 4096 8192 8192 16384 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 64 vector 64 vector->tiered 65 vector 64 33283408.221001834 574712643.67816091 76698880.196349144 1798561.1510791369 422047.77580822154 622556 2606581900541539285
103 baseline 4096 forced_vector_hash 5 14750161124092376284 vector vector 64 64 4096 8192 8192 16384 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 64 vector 64 vector 64 vector 64 17349966.167565972 660938532.71645737 78027465.667915106 297026.76211126626 421247.73579342011 623136 2606581900541539285
104 baseline 4096 adaptive_hash 6 5292099999092531180 vector tiered 64 65 4096 8192 8192 16384 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 64 vector 64 vector->tiered 65 vector 64 32252862.441541687 590318772.13695395 78740157.480314955 1836884.6436443792 420062.16920104175 626776 2608914160727435722
105 baseline 4096 forced_vector_hash 6 5292099999092531180 vector vector 64 64 4096 8192 8192 16384 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 64 vector 64 vector 64 vector 64 17002176.278563656 665778961.38482022 78486774.97841613 317783.14478200075 432656.94630727294 623136 2608914160727435722
106 baseline 4096 forced_tiered_hash 6 5292099999092531180 tiered tiered 64 65 4096 8192 8192 16384 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 64 tiered 64 tiered 65 tiered 64 32526671.870934166 45460744.646997318 55694792.536897801 1793078.7161556394 3138731.9522912744 626776 2608914160727435722
107 baseline 8192 forced_tiered_hash 0 1533354404713909492 tiered tiered 91 91 8192 16384 16384 32768 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 91 tiered 91 tiered 91 tiered 91 27054081.108135164 42929509.744998708 51714330.04085432 1309757.6948264572 2308402.5854108958 1237280 2600442685266303989
108 baseline 8192 adaptive_hash 0 1533354404713909492 tiered tiered 65 91 8192 16384 16384 32768 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 65 tiered 65 tiered 91 tiered 65 29148570.262628619 40794680.373679273 49875311.72069826 1308729.2239235702 2704895.861509332 1237280 2600442685266303989
109 baseline 8192 forced_vector_hash 0 1533354404713909492 vector vector 91 91 8192 16384 16384 32768 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 91 vector 91 vector 91 vector 91 8709511.6576813553 664893617.02127659 71813285.457809687 166747.26117623519 198736.03879327475 1245728 2600442685266303989
110 baseline 8192 adaptive_hash 1 8851810353577849580 tiered tiered 65 91 8192 16384 16384 32768 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 65 tiered 65 tiered 91 tiered 65 28164253.928913422 40543279.951348066 50296750.82989639 1333688.9837289944 2703433.3603676669 1238436 2610692047236465301
111 baseline 8192 forced_vector_hash 1 8851810353577849580 vector vector 91 91 8192 16384 16384 32768 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 91 vector 91 vector 91 vector 91 8783255.601521261 659630606.86015832 72385088.671733633 161540.44972861203 193757.14479471432 1245728 2610692047236465301
112 baseline 8192 forced_tiered_hash 1 8851810353577849580 tiered tiered 91 91 8192 16384 16384 32768 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 91 tiered 91 tiered 91 tiered 91 27512587.008556414 43410314.290675469 51245259.813467249 1348435.8144552319 2285714.2857142854 1238436 2610692047236465301
113 baseline 8192 forced_vector_hash 2 6074101987220463306 vector vector 91 91 8192 16384 16384 32768 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 91 vector 91 vector 91 vector 91 9024863.4989395794 654022236.75604963 71428571.428571433 163076.27077184001 199203.18725099601 1245728 2604400936210790827
114 baseline 8192 forced_tiered_hash 2 6074101987220463306 tiered tiered 91 91 8192 16384 16384 32768 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 91 tiered 91 tiered 91 tiered 91 28787747.934479084 42892682.508364074 51116904.360271946 1256597.1349585322 2222222.2222222225 1238436 2604400936210790827
115 baseline 8192 adaptive_hash 2 6074101987220463306 tiered tiered 65 91 8192 16384 16384 32768 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 65 tiered 65 tiered 91 tiered 65 28477047.499715228 40498947.027377293 50263885.398341291 1406074.24071991 2700513.097488523 1238436 2604400936210790827
116 baseline 8192 forced_vector_hash 3 2489097639920090271 vector vector 91 91 8192 16384 16384 32768 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 91 vector 91 vector 91 vector 91 8906543.6376105528 637348629.70044613 71813285.457809687 172161.48747525178 192267.02043798426 1245728 2603956742105246981
117 baseline 8192 forced_tiered_hash 3 2489097639920090271 tiered tiered 91 91 8192 16384 16384 32768 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 91 tiered 91 tiered 91 tiered 91 28417971.525192529 42078687.144961074 49578582.052553296 1286504.5670912133 2255808.7074216106 1237280 2603956742105246981
118 baseline 8192 adaptive_hash 3 2489097639920090271 tiered tiered 65 91 8192 16384 16384 32768 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 65 tiered 65 tiered 91 tiered 65 28468129.928544994 36736343.264391467 47705371.624844961 1366493.5774801858 2704895.861509332 1237280 2603956742105246981
119 baseline 8192 adaptive_hash 4 1885911782505072956 tiered tiered 65 91 8192 16384 16384 32768 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 65 tiered 65 tiered 91 tiered 65 28603300.820914734 40546567.733041398 50459178.524573617 1348617.6668914363 2722570.1061802343 1237280 2604212537617782366
120 baseline 8192 forced_vector_hash 4 1885911782505072956 vector vector 91 91 8192 16384 16384 32768 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 91 vector 91 vector 91 vector 91 8824956.9783347305 657894736.84210527 72663856.997529432 164395.27199197753 200952.51492072421 1245728 2604212537617782366
121 baseline 8192 forced_tiered_hash 4 1885911782505072956 tiered tiered 91 91 8192 16384 16384 32768 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 91 tiered 91 tiered 91 tiered 91 28479480.534275055 43012602.692588933 50306871.918704093 1317002.5023047545 2276867.0309653915 1237280 2604212537617782366
122 baseline 8192 forced_tiered_hash 5 2737113097762816153 tiered tiered 91 91 8192 16384 16384 32768 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 91 tiered 91 tiered 91 tiered 91 28498147.620404676 42098172.939294435 50671395.996959716 1381597.1262779774 2319109.4619666049 1235716 2608173366147803176
123 baseline 8192 adaptive_hash 5 2737113097762816153 tiered tiered 65 91 8192 16384 16384 32768 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 65 tiered 65 tiered 91 tiered 65 28656579.550664835 41177681.696520485 50357538.523516975 1332267.5193178789 2717391.3043478262 1235716 2608173366147803176
124 baseline 8192 forced_vector_hash 5 2737113097762816153 vector vector 91 91 8192 16384 16384 32768 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 91 vector 91 vector 91 vector 91 8791517.9434881229 650618087.18282366 75063804.23359856 167504.18760469012 200835.47557840619 1245728 2608173366147803176
125 baseline 8192 forced_vector_hash 6 16149500638224588516 vector vector 91 91 8192 16384 16384 32768 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 91 vector 91 vector 91 vector 91 8634907.4769663848 666222518.32111931 75035641.92991671 161269.51361114695 190901.62839089017 1245728 2604522381130608740
126 baseline 8192 forced_tiered_hash 6 16149500638224588516 tiered tiered 91 91 8192 16384 16384 32768 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 91 tiered 91 tiered 91 tiered 91 29339279.427297268 42990413.137870252 52446635.548329569 1346982.7586206896 2207018.3182520415 1235716 2604522381130608740
127 baseline 8192 adaptive_hash 6 16149500638224588516 tiered tiered 65 91 8192 16384 16384 32768 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 65 tiered 65 tiered 91 tiered 65 28920959.019001067 41523066.063198105 51002193.094303049 1426533.5235378032 2758620.6896551726 1235716 2604522381130608740
128 baseline 16384 forced_vector_hash 0 16519628863117178580 vector vector 128 128 16384 32768 32768 65536 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 128 vector 128 vector 128 vector 128 4489579.6855498385 653594771.24182999 78889239.50773114 83519.163472058674 96723.959492005772 2490912 2811432233702823938
129 baseline 16384 forced_tiered_hash 0 16519628863117178580 tiered tiered 128 129 16384 32768 32768 65536 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 128 tiered 128 tiered 129 tiered 128 23466466.419486552 40734856.816978291 44122837.980938934 923361.03416435828 1396453.0093562352 2465412 2811432233702823938
130 baseline 16384 adaptive_hash 0 16519628863117178580 tiered tiered 91 129 16384 32768 32768 65536 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 91 tiered 91 tiered 129 tiered 91 22854008.593107231 38134462.113411888 50867287.247571088 868357.06842653698 2057189.8786257971 2465412 2811432233702823938
131 baseline 16384 adaptive_hash 1 12150548782170805316 tiered tiered 91 129 16384 32768 32768 65536 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 91 tiered 91 tiered 129 tiered 91 23044660.552150067 35303254.960107327 51464155.215892136 836400.13382402144 2046245.1401677921 2465412 2809601574756132900
132 baseline 16384 forced_vector_hash 1 12150548782170805316 vector vector 128 128 16384 32768 32768 65536 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 128 vector 128 vector 128 vector 128 4505356.8693176182 649350649.35064936 79157761.418507084 87278.313084764697 96229.71958659713 2490912 2809601574756132900
133 baseline 16384 forced_tiered_hash 1 12150548782170805316 tiered tiered 128 129 16384 32768 32768 65536 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 128 tiered 128 tiered 129 tiered 128 22649543.611696225 41621576.625322565 50446451.092165664 837871.80561374116 1707941.9299743809 2465412 2809601574756132900
134 baseline 16384 adaptive_hash 2 14057633980897130801 tiered tiered 91 129 16384 32768 32768 65536 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 91 tiered 91 tiered 129 tiered 91 23025028.205659553 38423115.346192271 50658561.296859168 866776.45835139125 2076843.1983385254 2465412 2849474804600164814
135 baseline 16384 forced_vector_hash 2 14057633980897130801 vector vector 128 128 16384 32768 32768 65536 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 128 vector 128 vector 128 vector 128 4469873.0556052206 655307994.75753605 79955225.073958591 86878.708634874856 95758.840934989319 2490912 2849474804600164814
136 baseline 16384 forced_tiered_hash 2 14057633980897130801 tiered tiered 128 129 16384 32768 32768 65536 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 128 tiered 128 tiered 129 tiered 128 23206163.557040751 40086587.027980439 51802735.184417732 975704.94682408043 1707358.7160662457 2465412 2849474804600164814
137 baseline 16384 forced_tiered_hash 3 9496396460044610260 tiered tiered 128 129 16384 32768 32768 65536 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 128 tiered 128 tiered 129 tiered 128 23691629.747210313 40364898.684104308 51284681.265705928 956205.77548288403 1713796.0582690658 2465412 2809604592862375281
138 baseline 16384 adaptive_hash 3 9496396460044610260 tiered tiered 91 129 16384 32768 32768 65536 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 91 tiered 91 tiered 129 tiered 91 23141719.892622419 38822889.975929804 44738725.841088042 835073.06889352819 2125850.3401360544 2465412 2809604592862375281
139 baseline 16384 forced_vector_hash 3 9496396460044610260 vector vector 128 128 16384 32768 32768 65536 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 128 vector 128 vector 128 vector 128 4597257.2763089538 633312222.92590249 79674926.300693169 87185.4783867199 96414.350311900431 2490912 2809604592862375281
140 baseline 16384 adaptive_hash 4 14635596384326246855 tiered tiered 91 129 16384 32768 32768 65536 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 91 tiered 91 tiered 129 tiered 91 22955260.197874345 38213152.967251323 37296732.806206174 810964.2364771713 2092925.910422771 2465412 2802782570264501827
141 baseline 16384 forced_vector_hash 4 14635596384326246855 vector vector 128 128 16384 32768 32768 65536 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 128 vector 128 vector 128 vector 128 4496180.4946697783 651465798.04560268 79993600.511959031 84569.456894947827 94758.886014536023 2490912 2802782570264501827
142 baseline 16384 forced_tiered_hash 4 14635596384326246855 tiered tiered 128 129 16384 32768 32768 65536 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 128 tiered 128 tiered 129 tiered 128 23009134.626446698 41142104.830083102 51551706.361480564 936241.92491339764 1738828.0299078419 2465412 2802782570264501827
143 baseline 16384 adaptive_hash 5 2683868946381181637 tiered tiered 91 129 16384 32768 32768 65536 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 91 tiered 91 tiered 129 tiered 91 22778005.557833355 38630920.188518889 49967521.11127767 810832.72520878934 2072968.4908789385 2465412 2805449818878573377
144 baseline 16384 forced_vector_hash 5 2683868946381181637 vector vector 128 128 16384 32768 32768 65536 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 128 vector 128 vector 128 vector 128 4443634.7154518515 649350649.35064936 79478620.251152441 87065.542940725776 98948.180837695298 2490912 2805449818878573377
145 baseline 16384 forced_tiered_hash 5 2683868946381181637 tiered tiered 128 129 16384 32768 32768 65536 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 128 tiered 128 tiered 129 tiered 128 23255813.953488372 36956280.719908349 50045040.536482841 935628.74251497013 1709693.9647803043 2465412 2805449818878573377
146 baseline 16384 forced_tiered_hash 6 7266316803874473535 tiered tiered 128 129 16384 32768 32768 65536 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 128 tiered 128 tiered 129 tiered 128 23616654.464728527 40329085.33634457 51602249.858093813 929108.98448387987 1710863.9863130883 2465412 2802923093430382200
147 baseline 16384 adaptive_hash 6 7266316803874473535 tiered tiered 91 129 16384 32768 32768 65536 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 91 tiered 91 tiered 129 tiered 91 22878059.940517046 38170852.736850142 51559680.329981953 837100.28461409674 2079434.3938448743 2465412 2802923093430382200
148 baseline 16384 forced_vector_hash 6 7266316803874473535 vector vector 128 128 16384 32768 32768 65536 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 128 vector 128 vector 128 vector 128 4471492.0027365535 641436818.47338045 77754451.442345068 86972.403656319846 94894.666919719108 2490912 2802923093430382200
149 baseline 32768 forced_tiered_hash 0 3799899914257613626 tiered tiered 182 182 32768 65536 65536 131072 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 182 tiered 182 tiered 182 tiered 182 18081547.780490011 37212071.59602575 50994390.617032126 573000.22920009168 1245640.2590931738 4921232 5921195651255336034
150 baseline 32768 adaptive_hash 0 3799899914257613626 tiered tiered 129 182 32768 65536 65536 131072 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 129 tiered 129 tiered 182 tiered 129 18079586.339064565 35783296.357260428 48744820.862783328 509658.01946893631 1541544.6277169725 4921232 5921195651255336034
151 baseline 32768 forced_vector_hash 0 3799899914257613626 vector vector 182 182 32768 65536 65536 131072 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 182 vector 182 vector 182 vector 182 2246075.5445048637 637348629.70044613 80638658.172728002 43353.102348004024 48926.55146094683 4981280 5921195651255336034
152 baseline 32768 forced_vector_hash 1 14870762844571170300 vector vector 182 182 32768 65536 65536 131072 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 182 vector 182 vector 182 vector 182 2274650.3862356353 614250614.25061429 68898994.074686512 43827.547366621817 47624.263609823931 4981280 5903990191892135316
153 baseline 32768 forced_tiered_hash 1 14870762844571170300 tiered tiered 182 182 32768 65536 65536 131072 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 182 tiered 182 tiered 182 tiered 182 18031338.46625435 37676135.935498461 50994390.617032126 557631.18273573858 1253918.4952978056 4921232 5903990191892135316
154 baseline 32768 adaptive_hash 1 14870762844571170300 tiered tiered 129 182 32768 65536 65536 131072 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 129 tiered 129 tiered 182 tiered 129 16707321.148127109 35651894.898213841 49058084.772370487 519076.04464053985 1548227.2797646695 4921232 5903990191892135316
155 baseline 32768 adaptive_hash 2 3668356801526459156 tiered tiered 129 182 32768 65536 65536 131072 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 129 tiered 129 tiered 182 tiered 129 17825629.690368813 35951824.555096172 49748768.717974231 514165.2527122217 1523693.4328813043 4921232 5867651332793912201
156 baseline 32768 forced_vector_hash 2 3668356801526459156 vector vector 182 182 32768 65536 65536 131072 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 182 vector 182 vector 182 vector 182 2285824.0052665384 610873549.17532074 81254570.569594547 43017.968605486516 47917.734832838978 4981280 5867651332793912201
157 baseline 32768 forced_tiered_hash 2 3668356801526459156 tiered tiered 182 182 32768 65536 65536 131072 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 182 tiered 182 tiered 182 tiered 182 18491124.260355029 38431975.403535739 50266411.983512618 575373.99309551215 1244400.1991040318 4921232 5867651332793912201
158 baseline 32768 forced_vector_hash 3 5597019136430372439 vector vector 182 182 32768 65536 65536 131072 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 182 vector 182 vector 182 vector 182 2267244.6629060637 600240096.03841531 79687624.5119133 44299.339496848108 47770.548501437894 4981280 5952659854051489931
159 baseline 32768 forced_tiered_hash 3 5597019136430372439 tiered tiered 182 182 32768 65536 65536 131072 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 182 tiered 182 tiered 182 tiered 182 18790281.666322179 37717346.207520835 49287791.414066732 571004.3967338549 1260080.6451612904 4921232 5952659854051489931
160 baseline 32768 adaptive_hash 3 5597019136430372439 tiered tiered 129 182 32768 65536 65536 131072 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 129 tiered 129 tiered 182 tiered 129 18053800.324968409 33225902.913911685 48612123.863691606 489620.05483744608 1558360.6046439146 4921232 5952659854051489931
161 baseline 32768 adaptive_hash 4 6810547043850850325 tiered tiered 129 182 32768 65536 65536 131072 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 129 tiered 129 tiered 182 tiered 129 18033289.452328999 36264732.54759746 50125313.283208027 463306.15270570794 1557632.398753894 4921232 5886124142016643594
162 baseline 32768 forced_vector_hash 4 6810547043850850325 vector vector 182 182 32768 65536 65536 131072 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 182 vector 182 vector 182 vector 182 2236391.0016571656 609756097.56097555 68376068.376068369 42827.286120533114 48879.199945255292 4981280 5886124142016643594
163 baseline 32768 forced_tiered_hash 4 6810547043850850325 tiered tiered 182 182 32768 65536 65536 131072 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 182 tiered 182 tiered 182 tiered 182 18414849.734826162 36848699.240916796 50733093.196692206 578435.90930124943 1232134.056185313 4921232 5886124142016643594
164 baseline 32768 adaptive_hash 5 11482174959549226004 tiered tiered 129 182 32768 65536 65536 131072 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 129 tiered 129 tiered 182 tiered 129 17838667.094794676 36209581.055147186 49838026.414154001 525762.35541535227 1533036.9461904033 4921232 5854156252755476422
165 baseline 32768 forced_vector_hash 5 11482174959549226004 vector vector 182 182 32768 65536 65536 131072 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 182 vector 182 vector 182 vector 182 2227990.2414027425 589275191.5144372 77567483.710828424 42526.770602094017 47344.673960902765 4981280 5854156252755476422
166 baseline 32768 forced_tiered_hash 5 11482174959549226004 tiered tiered 182 182 32768 65536 65536 131072 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 182 tiered 182 tiered 182 tiered 182 18402988.645356007 33594248.664628617 49394912.32403063 519750.51975051977 1255492.7809165097 4921232 5854156252755476422
167 baseline 32768 adaptive_hash 6 4266179595843056063 tiered tiered 129 182 32768 65536 65536 131072 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 129 tiered 129 tiered 182 tiered 129 17943012.990741406 35944071.025484346 48683121.56175454 534702.17089081381 1548227.2797646695 4921232 5945944892063186673
168 baseline 32768 forced_vector_hash 6 4266179595843056063 vector vector 182 182 32768 65536 65536 131072 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 182 vector 182 vector 182 vector 182 2226794.573746983 617283950.61728394 81806282.722513095 42699.459851832878 47783.787916435707 4981280 5945944892063186673
169 baseline 32768 forced_tiered_hash 6 4266179595843056063 tiered tiered 182 182 32768 65536 65536 131072 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 182 tiered 182 tiered 182 tiered 182 18222238.419767484 37572797.294758596 49202912.812438495 552852.72003538255 1242081.7289777668 4921232 5945944892063186673
170 baseline 65536 forced_tiered_hash 0 10661120783772612247 tiered tiered 256 257 65536 131072 131072 262144 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 256 tiered 256 tiered 257 tiered 256 13402849.445792176 34753596.997289218 45568466.621098205 329261.46653057192 899523.25267608173 9837616 7078339330582418198
171 baseline 65536 adaptive_hash 0 10661120783772612247 tiered tiered 182 257 65536 131072 131072 262144 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 182 tiered 182 tiered 257 tiered 182 13422458.457491074 32863386.900653984 44482007.028157115 300237.18737802864 1125365.7438667566 9837616 7078339330582418198
172 baseline 65536 forced_vector_hash 0 10661120783772612247 vector vector 256 256 65536 131072 131072 262144 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 256 vector 256 vector 256 vector 256 1119194.1801902631 498256103.63726956 62523446.292359635 21548.705461519399 23725.130132338778 9962016 7078339330582418198
173 baseline 65536 forced_vector_hash 1 3645920298421955121 vector vector 256 256 65536 131072 131072 262144 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 256 vector 256 vector 256 vector 256 1040478.7867184964 374391613.62785476 44754744.002864301 21149.518636955821 22512.027050451703 9962016 7058462025128772180
174 baseline 65536 forced_tiered_hash 1 3645920298421955121 tiered tiered 256 257 65536 131072 131072 262144 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 256 tiered 256 tiered 257 tiered 256 10046818.17268471 27078989.412115142 41592147.402570397 285103.34996436205 820209.97375328094 9837616 7058462025128772180
175 baseline 65536 adaptive_hash 1 3645920298421955121 tiered tiered 182 257 65536 131072 131072 262144 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 182 tiered 182 tiered 257 tiered 182 11310937.676733401 31331265.469812326 43438599.539550841 283157.77551251557 1129177.9584462512 9837616 7058462025128772180
176 baseline 65536 forced_vector_hash 2 13440076570755554700 vector vector 256 256 65536 131072 131072 262144 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 256 vector 256 vector 256 vector 256 1047528.4613482949 359582883.85472852 65638332.786347233 21566.969754481619 23456.668496286809 9962016 7031035215144323638
177 baseline 65536 forced_tiered_hash 2 13440076570755554700 tiered tiered 256 257 65536 131072 131072 262144 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 256 tiered 256 tiered 257 tiered 256 13332800.02133248 34330049.091970205 45493835.585278191 315139.29156687256 912908.52656563802 9837616 7031035215144323638
178 baseline 65536 adaptive_hash 2 13440076570755554700 tiered tiered 182 257 65536 131072 131072 262144 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 182 tiered 182 tiered 257 tiered 182 12777429.947740313 25755273.392227057 22394911.876021765 282757.45065882488 1079447.322970639 9837616 7031035215144323638
179 baseline 65536 forced_tiered_hash 3 265097094473494960 tiered tiered 256 257 65536 131072 131072 262144 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 256 tiered 256 tiered 257 tiered 256 11666841.669291705 27756960.057734478 32832096.657692559 287811.19585551874 881057.26872246701 9837616 7048178356920180496
180 baseline 65536 adaptive_hash 3 265097094473494960 tiered tiered 182 257 65536 131072 131072 262144 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 182 tiered 182 tiered 257 tiered 182 9837387.9767444152 30628809.458176363 43387712.599791735 299976.0019198464 1097815.3474585575 9837616 7048178356920180496
181 baseline 65536 forced_vector_hash 3 265097094473494960 vector vector 256 256 65536 131072 131072 262144 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 256 vector 256 vector 256 vector 256 1099008.5843560523 490436488.47474253 71916576.770945698 22126.341409447949 22096.761719569997 9962016 7048178356920180496
182 baseline 65536 forced_tiered_hash 4 10511978180227547512 tiered tiered 256 257 65536 131072 131072 262144 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 256 tiered 256 tiered 257 tiered 256 12827748.986607831 33056758.454265974 44620945.071616612 324002.07361327112 912492.0156948627 9837616 7047216827085719657
183 baseline 65536 adaptive_hash 4 10511978180227547512 tiered tiered 182 257 65536 131072 131072 262144 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 182 tiered 182 tiered 257 tiered 182 11165449.632656708 24729826.643915229 42163848.716110803 281761.57335662562 1116819.2986374805 9837616 7047216827085719657
184 baseline 65536 forced_vector_hash 4 10511978180227547512 vector vector 256 256 65536 131072 131072 262144 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 256 vector 256 vector 256 vector 256 1096373.4160145072 491159135.55992144 60727515.637335271 21258.096677572074 22895.240537397087 9962016 7047216827085719657
185 baseline 65536 adaptive_hash 5 18171218181464882810 tiered tiered 182 257 65536 131072 131072 262144 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 182 tiered 182 tiered 257 tiered 182 13292216.078264568 31373533.287318818 38030043.734550297 295255.24816203606 986193.29388560168 9837616 6972502824690047116
186 baseline 65536 forced_vector_hash 5 18171218181464882810 vector vector 256 256 65536 131072 131072 262144 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 256 vector 256 vector 256 vector 256 1075244.5374889383 475285171.10266161 63678043.81049414 21506.810131428116 22458.535928042853 9962016 6972502824690047116
187 baseline 65536 forced_tiered_hash 5 18171218181464882810 tiered tiered 256 257 65536 131072 131072 262144 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 256 tiered 256 tiered 257 tiered 256 12614158.131086331 32799790.081343479 45798030.684680559 312460.94238220225 910166.56048056798 9837616 6972502824690047116
188 baseline 65536 forced_vector_hash 6 13709039232440699940 vector vector 256 256 65536 131072 131072 262144 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 vector 256 vector 256 vector 256 vector 256 1089099.2060466788 501253132.83208019 71169311.792754963 20818.283449256476 22991.835599178728 9962016 7010486178119326697
189 baseline 65536 forced_tiered_hash 6 13709039232440699940 tiered tiered 256 257 65536 131072 131072 262144 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 256 tiered 256 tiered 257 tiered 256 10754075.794726202 31665611.146295123 44218439.089100152 315666.52987783705 919793.96615158208 9837616 7010486178119326697
190 baseline 65536 adaptive_hash 6 13709039232440699940 tiered tiered 182 257 65536 131072 131072 262144 100000 97000 1000 500 500 1000 1000 100000 100000 1000 1000 tiered 182 tiered 182 tiered 257 tiered 182 13104614.134636804 29824038.174768861 42513391.718391292 311342.19620785204 1135331.5168029063 9837616 7010486178119326697
+1 -1
View File
@@ -730,7 +730,7 @@ private:
} }
return uc::CostModelPolicy(adaptation, config); return uc::CostModelPolicy(adaptation, config);
} }
uc::AdaptiveSequence<T> values_; uc::AdaptiveSequence<T, false, uc::CostModelPolicy> values_;
}; };
template <class Adapter, class T> template <class Adapter, class T>
+896
View File
@@ -0,0 +1,896 @@
#include "universal_container/adaptive_sequence.hpp"
#include <algorithm>
#include <bit>
#include <charconv>
#include <chrono>
#include <cmath>
#include <concepts>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <limits>
#include <optional>
#include <random>
#include <stdexcept>
#include <string>
#include <string_view>
#include <system_error>
#include <utility>
#include <vector>
#ifndef UC_BUILD_PROFILE
#define UC_BUILD_PROFILE "unknown"
#endif
namespace {
using Value = std::uint32_t;
using Clock = std::chrono::steady_clock;
// Every compared layout uses the same flat value index. Value lookup is thus
// comparable, and insert/erase timings include hash-index maintenance.
using Sequence = uc::AdaptiveSequence<Value, true>;
volatile std::uint64_t benchmark_sink = 0;
enum class OperationKind : std::uint8_t { read, find, insert, erase };
struct Operation {
OperationKind kind = OperationKind::read;
std::size_t index = 0;
Value value = 0;
};
struct FindQuery {
Value value = 0;
std::size_t expected_index = std::numeric_limits<std::size_t>::max();
};
struct Trace {
std::vector<Value> initial;
std::vector<Operation> mixed;
std::vector<std::size_t> reads;
std::vector<FindQuery> finds;
std::vector<Operation> inserts;
std::vector<Operation> erases;
std::size_t read_count = 0;
std::size_t find_count = 0;
std::size_t find_hit_count = 0;
std::size_t find_miss_count = 0;
std::size_t insert_count = 0;
std::size_t erase_count = 0;
std::uint64_t expected_mixed_guard = 0;
std::uint64_t expected_read_guard = 0;
std::uint64_t expected_find_guard = 0;
std::uint64_t expected_insert_guard = 0;
std::uint64_t expected_erase_guard = 0;
};
struct Options {
std::size_t min_n = 256;
std::size_t max_n = 65'536;
std::size_t operations = 100'000;
std::size_t repeats = 7;
std::filesystem::path output = "results/benchmarks/focused.csv";
std::uint64_t seed = 0x6a09'e667'f3bc'c909ULL;
};
struct Result {
std::size_t n = 0;
std::string container;
std::size_t repeat = 0;
std::uint64_t seed = 0;
std::string initial_mode;
std::string mode;
std::string read_mode;
std::string find_mode;
std::string insert_mode;
std::string erase_mode;
std::size_t read_leaf = 0;
std::size_t find_leaf = 0;
std::size_t insert_leaf = 0;
std::size_t erase_leaf = 0;
std::size_t initial_leaf = 0;
std::size_t leaf = 0;
std::optional<std::size_t> initial_logical_capacity;
std::optional<std::size_t> logical_capacity;
std::optional<std::size_t> initial_hash_buckets;
std::optional<std::size_t> hash_buckets;
std::size_t operations = 0;
std::size_t read_count = 0;
std::size_t find_count = 0;
std::size_t find_hit_count = 0;
std::size_t find_miss_count = 0;
std::size_t insert_count = 0;
std::size_t erase_count = 0;
std::size_t read_batch_count = 0;
std::size_t find_batch_count = 0;
std::size_t insert_batch_count = 0;
std::size_t erase_batch_count = 0;
double mixed_ops_per_sec = 0.0;
double read_ops_per_sec = 0.0;
double find_ops_per_sec = 0.0;
double insert_ops_per_sec = 0.0;
double erase_ops_per_sec = 0.0;
std::size_t bytes = 0;
std::uint64_t checksum = 0;
};
constexpr std::size_t missing_index = std::numeric_limits<std::size_t>::max();
constexpr std::size_t minimum_fast_batch_operations = 100'000;
[[noreturn]] void usage_error(std::string_view message) {
throw std::invalid_argument(std::string(message)
+ "\nusage: uc_focused_bench [--min-n N] [--max-n N]"
" [--operations N] [--repeats N] [--output FILE] [--seed N]");
}
template <class Integer>
Integer parse_integer(std::string_view text, std::string_view option) {
Integer value{};
const auto* first = text.data();
const auto* last = first + text.size();
const auto [end, error] = std::from_chars(first, last, value);
if (error != std::errc{} || end != last) {
usage_error("invalid integer for " + std::string(option));
}
return value;
}
Options parse_options(int argc, char** argv) {
Options options;
for (int i = 1; i < argc; ++i) {
const std::string_view option(argv[i]);
if (option == "--help" || option == "-h") {
std::cout
<< "usage: uc_focused_bench [--min-n N] [--max-n N]"
" [--operations N] [--repeats N] [--output FILE] [--seed N]\n";
std::exit(0);
}
if (i + 1 == argc) {
usage_error("missing value for " + std::string(option));
}
const std::string_view value(argv[++i]);
if (option == "--min-n") {
options.min_n = parse_integer<std::size_t>(value, option);
} else if (option == "--max-n") {
options.max_n = parse_integer<std::size_t>(value, option);
} else if (option == "--operations") {
options.operations = parse_integer<std::size_t>(value, option);
} else if (option == "--repeats") {
options.repeats = parse_integer<std::size_t>(value, option);
} else if (option == "--output") {
options.output = std::filesystem::path(value);
} else if (option == "--seed") {
options.seed = parse_integer<std::uint64_t>(value, option);
} else {
usage_error("unknown option: " + std::string(option));
}
}
if (options.min_n == 0 || options.max_n < options.min_n) {
usage_error("the size range must satisfy 1 <= min-n <= max-n");
}
if (options.operations == 0 || options.operations % 100 != 0) {
usage_error("--operations must be a positive multiple of 100");
}
if (options.repeats == 0) {
usage_error("--repeats must be positive");
}
if (options.max_n >= (std::uint64_t{1} << 31)
|| options.operations / 100 >= (std::uint64_t{1} << 31) - options.max_n) {
usage_error("size plus inserted values must stay below 2^31");
}
return options;
}
std::vector<std::size_t> power_of_two_sizes(const Options& options) {
std::vector<std::size_t> sizes;
std::size_t n = std::bit_ceil(options.min_n);
while (n <= options.max_n) {
sizes.push_back(n);
if (n > options.max_n / 2) {
break;
}
n *= 2;
}
if (sizes.empty()) {
usage_error("the requested range contains no power-of-two size");
}
return sizes;
}
std::size_t ceil_sqrt(std::size_t value) {
auto root = static_cast<std::size_t>(std::sqrt(static_cast<long double>(value)));
while (root * root < value) {
++root;
}
while (root > 1 && (root - 1) * (root - 1) >= value) {
--root;
}
return root;
}
std::size_t random_index(std::mt19937_64& random, std::size_t upper_exclusive) {
if (upper_exclusive == 0) {
throw std::logic_error("cannot select an index from an empty sequence");
}
return static_cast<std::size_t>(random() % upper_exclusive);
}
std::uint64_t guard_start(std::uint64_t tag) noexcept {
return 0xcbf2'9ce4'8422'2325ULL ^ (tag * 0x9e37'79b9'7f4a'7c15ULL);
}
void guard_event(std::uint64_t& guard,
std::uint64_t tag,
std::size_t index,
Value value) noexcept {
// This executes in timed batches, so keep the guard much cheaper than the
// operation being measured. The full ordered sequence digest below gives
// the structural checks a stronger second line of defence.
const auto word = static_cast<std::uint64_t>(index)
^ (static_cast<std::uint64_t>(value) << 17)
^ (tag << 3);
guard += std::rotl(word, static_cast<int>(tag & 7U));
guard ^= guard >> 29;
}
template <class Container>
std::uint64_t sequence_digest(const Container& container) {
auto digest = guard_start(0x51);
std::size_t index = 0;
if constexpr (requires(const Container& value) {
value.for_each([](const Value&) {});
}) {
container.for_each([&](const Value& value) {
guard_event(digest, 0x52, index++, value);
});
} else {
for (const auto value : container) {
guard_event(digest, 0x52, index++, value);
}
}
guard_event(digest, 0x53, container.size(), static_cast<Value>(container.size()));
return digest;
}
void finish_guard(std::uint64_t& guard, std::uint64_t state_digest) noexcept {
guard_event(guard, 0x7f, static_cast<std::size_t>(state_digest),
static_cast<Value>(state_digest >> 32));
}
Trace make_trace(std::size_t n, std::size_t operation_count, std::uint64_t seed) {
Trace trace;
trace.initial.resize(n);
for (std::size_t i = 0; i < n; ++i) {
trace.initial[i] = static_cast<Value>(i);
}
trace.find_count = operation_count / 100;
trace.insert_count = operation_count / 100;
trace.erase_count = operation_count / 100;
trace.read_count = operation_count - trace.find_count
- trace.insert_count - trace.erase_count;
trace.find_hit_count = (trace.find_count + 1) / 2;
trace.find_miss_count = trace.find_count - trace.find_hit_count;
std::mt19937_64 random(seed);
std::vector<OperationKind> kinds;
kinds.reserve(operation_count);
kinds.insert(kinds.end(), trace.read_count, OperationKind::read);
kinds.insert(kinds.end(), trace.find_count, OperationKind::find);
kinds.insert(kinds.end(), trace.insert_count, OperationKind::insert);
kinds.insert(kinds.end(), trace.erase_count, OperationKind::erase);
std::shuffle(kinds.begin(), kinds.end(), random);
trace.expected_mixed_guard = guard_start(1);
auto mixed_shadow = trace.initial;
trace.mixed.reserve(operation_count);
std::size_t find_ordinal = 0;
std::size_t insert_ordinal = 0;
for (std::size_t operation_index = 0; operation_index < kinds.size(); ++operation_index) {
if (kinds[operation_index] == OperationKind::erase && mixed_shadow.size() == 1) {
const auto next_insert = std::find(kinds.begin()
+ static_cast<std::ptrdiff_t>(operation_index + 1),
kinds.end(), OperationKind::insert);
if (next_insert == kinds.end()) {
throw std::logic_error("trace scheduling could not keep the sequence non-empty");
}
std::iter_swap(kinds.begin() + static_cast<std::ptrdiff_t>(operation_index),
next_insert);
}
Operation operation;
operation.kind = kinds[operation_index];
switch (operation.kind) {
case OperationKind::read:
operation.index = random_index(random, mixed_shadow.size());
operation.value = mixed_shadow[operation.index];
guard_event(trace.expected_mixed_guard, 1, operation.index, operation.value);
break;
case OperationKind::find:
if ((find_ordinal++ & 1U) == 0) {
operation.index = random_index(random, mixed_shadow.size());
operation.value = mixed_shadow[operation.index];
} else {
operation.index = missing_index;
operation.value = static_cast<Value>(
std::numeric_limits<Value>::max() - find_ordinal);
}
guard_event(trace.expected_mixed_guard, 2, operation.index, operation.value);
break;
case OperationKind::insert:
operation.index = static_cast<std::size_t>(random() % (mixed_shadow.size() + 1));
operation.value = static_cast<Value>(n + insert_ordinal++);
mixed_shadow.insert(mixed_shadow.begin()
+ static_cast<std::ptrdiff_t>(operation.index), operation.value);
guard_event(trace.expected_mixed_guard, 3, operation.index, operation.value);
break;
case OperationKind::erase:
operation.index = random_index(random, mixed_shadow.size());
operation.value = mixed_shadow[operation.index];
mixed_shadow.erase(mixed_shadow.begin()
+ static_cast<std::ptrdiff_t>(operation.index));
guard_event(trace.expected_mixed_guard, 4, operation.index, operation.value);
break;
}
trace.mixed.push_back(operation);
}
auto mixed_digest = guard_start(0x51);
for (std::size_t i = 0; i < mixed_shadow.size(); ++i) {
guard_event(mixed_digest, 0x52, i, mixed_shadow[i]);
}
guard_event(mixed_digest, 0x53, mixed_shadow.size(),
static_cast<Value>(mixed_shadow.size()));
finish_guard(trace.expected_mixed_guard, mixed_digest);
const auto read_batch_count = std::max(
trace.read_count, minimum_fast_batch_operations);
trace.expected_read_guard = guard_start(2);
trace.reads.reserve(read_batch_count);
for (std::size_t i = 0; i < read_batch_count; ++i) {
const auto index = random_index(random, trace.initial.size());
trace.reads.push_back(index);
guard_event(trace.expected_read_guard, 1, index, trace.initial[index]);
}
auto initial_digest = guard_start(0x51);
for (std::size_t i = 0; i < trace.initial.size(); ++i) {
guard_event(initial_digest, 0x52, i, trace.initial[i]);
}
guard_event(initial_digest, 0x53, trace.initial.size(),
static_cast<Value>(trace.initial.size()));
finish_guard(trace.expected_read_guard, initial_digest);
const auto find_batch_count = std::max(
trace.find_count, minimum_fast_batch_operations);
trace.expected_find_guard = guard_start(3);
trace.finds.reserve(find_batch_count);
for (std::size_t i = 0; i < find_batch_count; ++i) {
FindQuery query;
if ((i & 1U) == 0) {
query.expected_index = random_index(random, trace.initial.size());
query.value = trace.initial[query.expected_index];
} else {
query.value = static_cast<Value>(std::numeric_limits<Value>::max() - i);
}
trace.finds.push_back(query);
guard_event(trace.expected_find_guard, 2, query.expected_index, query.value);
}
finish_guard(trace.expected_find_guard, initial_digest);
trace.expected_insert_guard = guard_start(4);
auto insert_shadow = trace.initial;
trace.inserts.reserve(trace.insert_count);
for (std::size_t i = 0; i < trace.insert_count; ++i) {
Operation operation;
operation.kind = OperationKind::insert;
operation.index = static_cast<std::size_t>(random() % (insert_shadow.size() + 1));
operation.value = static_cast<Value>(n + i);
trace.inserts.push_back(operation);
insert_shadow.insert(insert_shadow.begin()
+ static_cast<std::ptrdiff_t>(operation.index), operation.value);
guard_event(trace.expected_insert_guard, 3, operation.index, operation.value);
}
auto insert_digest = guard_start(0x51);
for (std::size_t i = 0; i < insert_shadow.size(); ++i) {
guard_event(insert_digest, 0x52, i, insert_shadow[i]);
}
guard_event(insert_digest, 0x53, insert_shadow.size(),
static_cast<Value>(insert_shadow.size()));
finish_guard(trace.expected_insert_guard, insert_digest);
trace.expected_erase_guard = guard_start(5);
auto erase_shadow = trace.initial;
const auto erase_batch_count = std::min(trace.erase_count, erase_shadow.size());
trace.erases.reserve(erase_batch_count);
for (std::size_t i = 0; i < erase_batch_count; ++i) {
Operation operation;
operation.kind = OperationKind::erase;
operation.index = random_index(random, erase_shadow.size());
operation.value = erase_shadow[operation.index];
trace.erases.push_back(operation);
erase_shadow.erase(erase_shadow.begin()
+ static_cast<std::ptrdiff_t>(operation.index));
guard_event(trace.expected_erase_guard, 4, operation.index, operation.value);
}
auto erase_digest = guard_start(0x51);
for (std::size_t i = 0; i < erase_shadow.size(); ++i) {
guard_event(erase_digest, 0x52, i, erase_shadow[i]);
}
guard_event(erase_digest, 0x53, erase_shadow.size(),
static_cast<Value>(erase_shadow.size()));
finish_guard(trace.expected_erase_guard, erase_digest);
return trace;
}
template <class Container>
Value indexed_read(Container& container, std::size_t index) {
return static_cast<Value>(container[index]);
}
template <class Container>
void insert_at(Container& container, std::size_t index, Value value) {
if constexpr (std::same_as<Container, std::vector<Value>>) {
container.insert(container.begin() + static_cast<std::ptrdiff_t>(index), value);
} else {
container.insert(index, value);
}
}
template <class Container>
void erase_at(Container& container, std::size_t index) {
if constexpr (std::same_as<Container, std::vector<Value>>) {
container.erase(container.begin() + static_cast<std::ptrdiff_t>(index));
} else {
container.erase(index);
}
}
template <class Container>
std::size_t find_value(const Container& container, Value value) {
if constexpr (std::same_as<Container, std::vector<Value>>) {
const auto found = std::find(container.begin(), container.end(), value);
return found == container.end()
? missing_index
: static_cast<std::size_t>(found - container.begin());
} else {
return container.find_one(value).value_or(missing_index);
}
}
template <class Container>
std::optional<std::size_t> logical_capacity(const Container& container) {
if constexpr (requires(const Container& value) {
{ value.capacity() } -> std::convertible_to<std::size_t>;
}) {
return static_cast<std::size_t>(container.capacity());
} else {
return std::nullopt;
}
}
template <class Container>
std::optional<std::size_t> hash_bucket_count(const Container& container) {
if constexpr (requires(const Container& value) {
{ value.hash_bucket_count() } -> std::convertible_to<std::size_t>;
}) {
return static_cast<std::size_t>(container.hash_bucket_count());
} else {
return std::nullopt;
}
}
template <class Container>
std::size_t allocated_bytes(const Container& container) {
if constexpr (requires(const Container& value) {
{ value.allocated_bytes() } -> std::convertible_to<std::size_t>;
}) {
return static_cast<std::size_t>(container.allocated_bytes());
} else {
return sizeof(container) + container.capacity() * sizeof(Value);
}
}
template <class Container>
std::string storage_mode(const Container& container) {
if constexpr (requires(const Container& value) { value.mode(); }) {
return container.mode() == uc::StorageMode::vector ? "vector" : "tiered";
} else {
return "vector";
}
}
template <class Container>
std::size_t active_leaf(const Container& container) {
if constexpr (requires(const Container& value) { value.tiered_config(); }) {
return container.tiered_config().leaf_capacity;
} else {
return 0;
}
}
template <class Container>
std::string mode_transition(const Container& container,
std::string_view initial_mode) {
const auto final_mode = storage_mode(container);
return final_mode == initial_mode
? final_mode : std::string(initial_mode) + "->" + final_mode;
}
double operations_per_second(std::size_t count, Clock::duration duration) {
const auto seconds = std::chrono::duration<double>(duration).count();
return seconds > 0.0 ? static_cast<double>(count) / seconds : 0.0;
}
template <class Builder>
Result run_candidate(const Trace& trace,
std::size_t n,
std::size_t repeat,
std::uint64_t seed,
std::string container_name,
Builder&& builder) {
Result result;
result.n = n;
result.container = std::move(container_name);
result.repeat = repeat;
result.seed = seed;
result.operations = trace.mixed.size();
result.read_count = trace.read_count;
result.find_count = trace.find_count;
result.find_hit_count = trace.find_hit_count;
result.find_miss_count = trace.find_miss_count;
result.insert_count = trace.insert_count;
result.erase_count = trace.erase_count;
result.read_batch_count = trace.reads.size();
result.find_batch_count = trace.finds.size();
result.insert_batch_count = trace.inserts.size();
result.erase_batch_count = trace.erases.size();
auto mixed_container = builder(); // Construction is deliberately outside the timer.
result.initial_mode = storage_mode(mixed_container);
result.initial_leaf = active_leaf(mixed_container);
result.initial_logical_capacity = logical_capacity(mixed_container);
result.initial_hash_buckets = hash_bucket_count(mixed_container);
auto mixed_guard = guard_start(1);
const auto mixed_begin = Clock::now();
for (const auto& operation : trace.mixed) {
switch (operation.kind) {
case OperationKind::read: {
const auto value = indexed_read(mixed_container, operation.index);
guard_event(mixed_guard, 1, operation.index, value);
break;
}
case OperationKind::find: {
const auto index = find_value(mixed_container, operation.value);
guard_event(mixed_guard, 2, index, operation.value);
break;
}
case OperationKind::insert:
insert_at(mixed_container, operation.index, operation.value);
guard_event(mixed_guard, 3, operation.index, operation.value);
break;
case OperationKind::erase:
erase_at(mixed_container, operation.index);
guard_event(mixed_guard, 4, operation.index, operation.value);
break;
}
}
const auto mixed_end = Clock::now();
result.mixed_ops_per_sec = operations_per_second(trace.mixed.size(), mixed_end - mixed_begin);
result.mode = storage_mode(mixed_container);
result.leaf = active_leaf(mixed_container);
result.logical_capacity = logical_capacity(mixed_container);
result.hash_buckets = hash_bucket_count(mixed_container);
result.bytes = allocated_bytes(mixed_container);
finish_guard(mixed_guard, sequence_digest(mixed_container));
if (mixed_guard != trace.expected_mixed_guard) {
throw std::runtime_error(result.container + ": mixed workload checksum mismatch");
}
auto read_container = builder();
const auto read_initial_mode = storage_mode(read_container);
auto read_guard = guard_start(2);
const auto read_begin = Clock::now();
for (const auto index : trace.reads) {
guard_event(read_guard, 1, index, indexed_read(read_container, index));
}
const auto read_end = Clock::now();
result.read_ops_per_sec = operations_per_second(trace.reads.size(), read_end - read_begin);
result.read_mode = mode_transition(read_container, read_initial_mode);
result.read_leaf = active_leaf(read_container);
finish_guard(read_guard, sequence_digest(read_container));
if (read_guard != trace.expected_read_guard) {
throw std::runtime_error(result.container + ": read batch checksum mismatch");
}
auto find_container = builder();
const auto find_initial_mode = storage_mode(find_container);
auto find_guard = guard_start(3);
const auto find_begin = Clock::now();
for (const auto& query : trace.finds) {
guard_event(find_guard, 2, find_value(find_container, query.value), query.value);
}
const auto find_end = Clock::now();
result.find_ops_per_sec = operations_per_second(trace.finds.size(), find_end - find_begin);
result.find_mode = mode_transition(find_container, find_initial_mode);
result.find_leaf = active_leaf(find_container);
finish_guard(find_guard, sequence_digest(find_container));
if (find_guard != trace.expected_find_guard) {
throw std::runtime_error(result.container + ": find batch checksum mismatch");
}
auto insert_container = builder();
const auto insert_initial_mode = storage_mode(insert_container);
auto insert_guard = guard_start(4);
const auto insert_begin = Clock::now();
for (const auto& operation : trace.inserts) {
insert_at(insert_container, operation.index, operation.value);
guard_event(insert_guard, 3, operation.index, operation.value);
}
const auto insert_end = Clock::now();
result.insert_ops_per_sec = operations_per_second(trace.inserts.size(), insert_end - insert_begin);
result.insert_mode = mode_transition(insert_container, insert_initial_mode);
result.insert_leaf = active_leaf(insert_container);
finish_guard(insert_guard, sequence_digest(insert_container));
if (insert_guard != trace.expected_insert_guard) {
throw std::runtime_error(result.container + ": insert batch checksum mismatch");
}
auto erase_container = builder();
const auto erase_initial_mode = storage_mode(erase_container);
auto erase_guard = guard_start(5);
const auto erase_begin = Clock::now();
for (const auto& operation : trace.erases) {
erase_at(erase_container, operation.index);
guard_event(erase_guard, 4, operation.index, operation.value);
}
const auto erase_end = Clock::now();
result.erase_ops_per_sec = operations_per_second(trace.erases.size(), erase_end - erase_begin);
result.erase_mode = mode_transition(erase_container, erase_initial_mode);
result.erase_leaf = active_leaf(erase_container);
finish_guard(erase_guard, sequence_digest(erase_container));
if (erase_guard != trace.expected_erase_guard) {
throw std::runtime_error(result.container + ": erase batch checksum mismatch");
}
result.checksum = mixed_guard;
guard_event(result.checksum, 0x61, static_cast<std::size_t>(read_guard),
static_cast<Value>(read_guard >> 32));
guard_event(result.checksum, 0x62, static_cast<std::size_t>(find_guard),
static_cast<Value>(find_guard >> 32));
guard_event(result.checksum, 0x63, static_cast<std::size_t>(insert_guard),
static_cast<Value>(insert_guard >> 32));
guard_event(result.checksum, 0x64, static_cast<std::size_t>(erase_guard),
static_cast<Value>(erase_guard >> 32));
benchmark_sink ^= result.checksum;
return result;
}
Sequence build_forced_vector(const std::vector<Value>& initial, uc::TieredConfig config) {
Sequence sequence(config);
sequence.force_vector_mode();
for (const auto value : initial) {
sequence.push_back(value);
}
return sequence;
}
Sequence build_forced_tiered(const std::vector<Value>& initial, uc::TieredConfig config) {
auto sequence = build_forced_vector(initial, config);
sequence.force_tiered_mode(config);
return sequence;
}
Sequence build_adaptive(const std::vector<Value>& initial, uc::TieredConfig config) {
Sequence sequence(config);
for (const auto value : initial) {
sequence.push_back(value);
}
sequence.enable_auto_mode();
(void)sequence.adapt_now();
return sequence;
}
std::string csv_string(std::string_view text) {
std::string result;
result.reserve(text.size() + 2);
result.push_back('"');
for (const auto character : text) {
if (character == '"') {
result.push_back('"');
}
result.push_back(character);
}
result.push_back('"');
return result;
}
void write_csv(const std::filesystem::path& path, const std::vector<Result>& results) {
if (!path.parent_path().empty()) {
std::filesystem::create_directories(path.parent_path());
}
std::ofstream output(path, std::ios::binary);
if (!output) {
throw std::runtime_error("cannot open output file: " + path.string());
}
output
<< "profile,n,container,repeat,seed,initial_mode,mixed_mode,initial_leaf,mixed_leaf,"
"initial_logical_capacity,logical_capacity,initial_hash_buckets,hash_buckets,operations,"
"read_count,find_count,find_hit_count,find_miss_count,insert_count,erase_count,"
"read_batch_count,find_batch_count,insert_batch_count,erase_batch_count,"
"read_mode,read_leaf,find_mode,find_leaf,insert_mode,insert_leaf,erase_mode,erase_leaf,"
"mixed_ops_per_sec,read_ops_per_sec,find_ops_per_sec,insert_ops_per_sec,"
"erase_ops_per_sec,bytes,checksum\n";
output << std::setprecision(17);
for (const auto& result : results) {
output << csv_string(UC_BUILD_PROFILE) << ',' << result.n << ','
<< csv_string(result.container) << ',' << result.repeat << ','
<< result.seed << ',' << csv_string(result.initial_mode) << ','
<< csv_string(result.mode) << ',' << result.initial_leaf << ','
<< result.leaf << ',';
if (result.initial_logical_capacity) {
output << *result.initial_logical_capacity;
}
output << ',';
if (result.logical_capacity) {
output << *result.logical_capacity;
}
output << ',';
if (result.initial_hash_buckets) {
output << *result.initial_hash_buckets;
}
output << ',';
if (result.hash_buckets) {
output << *result.hash_buckets;
}
output << ',' << result.operations << ',' << result.read_count << ','
<< result.find_count << ',' << result.find_hit_count << ','
<< result.find_miss_count << ',' << result.insert_count << ','
<< result.erase_count << ',' << result.read_batch_count << ','
<< result.find_batch_count << ',' << result.insert_batch_count << ','
<< result.erase_batch_count << ',' << csv_string(result.read_mode) << ','
<< result.read_leaf << ',' << csv_string(result.find_mode) << ','
<< result.find_leaf << ',' << csv_string(result.insert_mode) << ','
<< result.insert_leaf << ',' << csv_string(result.erase_mode) << ','
<< result.erase_leaf << ',' << result.mixed_ops_per_sec << ','
<< result.read_ops_per_sec << ',' << result.find_ops_per_sec << ','
<< result.insert_ops_per_sec << ',' << result.erase_ops_per_sec << ','
<< result.bytes << ',' << result.checksum << '\n';
}
}
double median(std::vector<double> values) {
std::sort(values.begin(), values.end());
const auto middle = values.size() / 2;
if ((values.size() & 1U) != 0) {
return values[middle];
}
return (values[middle - 1] + values[middle]) / 2.0;
}
void print_summary(const std::vector<Result>& results,
const std::vector<std::size_t>& sizes) {
constexpr std::string_view containers[] = {
"forced_vector_hash", "forced_tiered_hash", "adaptive_hash"
};
if (!results.empty()) {
const auto& sample = results.front();
std::cout << "\nMixed trace: " << sample.read_count << " indexed reads, "
<< sample.find_count << " flat-hash find_one calls ("
<< sample.find_hit_count << " hit / " << sample.find_miss_count
<< " miss), " << sample.insert_count << " inserts, "
<< sample.erase_count << " erases.\n";
}
std::cout
<< "\nMedian throughput (operations/second)\n"
"| n | container | mixed mode | mixed leaf | mixed ops/s | read ops/s | find ops/s | insert ops/s | erase ops/s |\n"
"|---:|:----------|:-----|-----:|------------:|-----------:|-----------:|-------------:|------------:|\n";
for (const auto n : sizes) {
for (const auto name : containers) {
std::vector<double> mixed;
std::vector<double> reads;
std::vector<double> finds;
std::vector<double> inserts;
std::vector<double> erases;
const Result* representative = nullptr;
for (const auto& result : results) {
if (result.n != n || result.container != name) {
continue;
}
representative = &result;
mixed.push_back(result.mixed_ops_per_sec);
reads.push_back(result.read_ops_per_sec);
finds.push_back(result.find_ops_per_sec);
inserts.push_back(result.insert_ops_per_sec);
erases.push_back(result.erase_ops_per_sec);
}
if (!representative) {
continue;
}
const auto displayed_mode = representative->initial_mode == representative->mode
? representative->mode
: representative->initial_mode + "->" + representative->mode;
std::cout << "| " << n << " | " << name << " | "
<< displayed_mode << " | " << representative->leaf << " | "
<< std::fixed << std::setprecision(0)
<< median(std::move(mixed)) << " | " << median(std::move(reads)) << " | "
<< median(std::move(finds)) << " | " << median(std::move(inserts)) << " | "
<< median(std::move(erases)) << " |\n";
}
}
}
std::uint64_t repeat_seed(std::uint64_t base, std::size_t n, std::size_t repeat) noexcept {
auto value = base ^ (static_cast<std::uint64_t>(n) * 0x9e37'79b9'7f4a'7c15ULL);
value ^= static_cast<std::uint64_t>(repeat + 1) * 0xbf58'476d'1ce4'e5b9ULL;
value ^= value >> 30;
value *= 0xbf58'476d'1ce4'e5b9ULL;
value ^= value >> 27;
value *= 0x94d0'49bb'1331'11ebULL;
return value ^ (value >> 31);
}
} // namespace
int main(int argc, char** argv) {
try {
const auto options = parse_options(argc, argv);
const auto sizes = power_of_two_sizes(options);
std::vector<Result> results;
results.reserve(sizes.size() * options.repeats * 3);
const auto total_cells = sizes.size() * options.repeats;
std::size_t completed_cells = 0;
for (const auto n : sizes) {
const uc::TieredConfig config{ceil_sqrt(n), 64, 4};
for (std::size_t repeat = 0; repeat < options.repeats; ++repeat) {
const auto seed = repeat_seed(options.seed, n, repeat);
const auto trace = make_trace(n, options.operations, seed);
// Rotate the candidate order per paired repeat so cache state,
// thermal drift, and a fixed first/last position do not always
// favour the same representation.
const auto first_candidate = static_cast<std::size_t>(seed % 3);
for (std::size_t offset = 0; offset < 3; ++offset) {
switch ((first_candidate + offset) % 3) {
case 0:
results.push_back(run_candidate(
trace, n, repeat, seed, "forced_vector_hash",
[&] { return build_forced_vector(trace.initial, config); }));
break;
case 1:
results.push_back(run_candidate(
trace, n, repeat, seed, "forced_tiered_hash",
[&] { return build_forced_tiered(trace.initial, config); }));
break;
default:
results.push_back(run_candidate(
trace, n, repeat, seed, "adaptive_hash",
[&] { return build_adaptive(trace.initial, config); }));
break;
}
}
++completed_cells;
const auto percent = completed_cells * 100 / total_cells;
std::cerr << "\r[" << std::setw(3) << percent << "%] n=" << n
<< " repeat=" << (repeat + 1) << '/' << options.repeats << std::flush;
}
}
std::cerr << '\n';
write_csv(options.output, results);
print_summary(results, sizes);
std::cout << "\nRaw repeat data: " << options.output.string() << '\n';
return benchmark_sink == 0x0123'4567'89ab'cdefULL ? 2 : 0;
} catch (const std::exception& error) {
std::cerr << "uc_focused_bench: " << error.what() << '\n';
return 1;
}
}
+249 -96
View File
@@ -5,6 +5,7 @@
#include <cstdint> #include <cstdint>
#include <cstdlib> #include <cstdlib>
#include <iostream> #include <iostream>
#include <limits>
#include <optional> #include <optional>
#include <random> #include <random>
#include <stdexcept> #include <stdexcept>
@@ -72,6 +73,12 @@ struct DeferredTransitionPolicy {
} }
}; };
struct WraparoundHash {
[[nodiscard]] std::size_t operator()(std::uint32_t) const noexcept {
return std::numeric_limits<std::size_t>::max();
}
};
template <class Sequence, class Reference> template <class Sequence, class Reference>
void expect_equal(const Sequence& sequence, const Reference& reference) { void expect_equal(const Sequence& sequence, const Reference& reference) {
EXPECT(sequence.size() == reference.size()); EXPECT(sequence.size() == reference.size());
@@ -172,6 +179,157 @@ void indexed_duplicates_and_ids() {
EXPECT(values.size() == 2); EXPECT(values.size() == 2);
} }
void indexed_ids_reuse_slots_without_reviving_stale_handles() {
uc::AdaptiveSequence<std::uint32_t, true> values;
for (std::uint32_t value = 0; value < 64; ++value) {
values.push_back(value);
}
const auto first_id = values.id_at(0);
const auto fixed_allocation = values.allocated_bytes();
for (std::uint32_t step = 0; step < 2'048; ++step) {
const auto retired = values.id_at(0);
values.erase_by_id(retired);
EXPECT(!values.id_alive(retired));
values.push_back(10'000 + step);
const auto replacement = values.id_at(values.size() - 1);
EXPECT(replacement != retired);
EXPECT(values.id_alive(replacement));
EXPECT(!values.id_alive(retired));
EXPECT(!values.id_alive(first_id));
EXPECT(values.find_one(10'000 + step) == values.size() - 1);
EXPECT(values.find_all_ids(10'000 + step)
== std::vector<decltype(values)::stable_id>({replacement}));
bool stale_rejected = false;
try {
values.erase_by_id(retired);
} catch (const std::out_of_range&) {
stale_rejected = true;
}
EXPECT(stale_rejected);
}
// Churn at a fixed live size must recycle the 64 metadata slots instead
// of growing the ID vector outside a logical-capacity boundary.
EXPECT(values.size() == 64);
EXPECT(values.capacity() == 64);
EXPECT(values.allocated_bytes() == fixed_allocation);
values.set(0, 0xf00d);
values.set(1, 0xf00d);
const auto duplicate_ids = values.find_all_ids(0xf00d);
EXPECT(duplicate_ids.size() == 2);
EXPECT(values.erase_all(0xf00d) == 2);
for (const auto id : duplicate_ids) {
EXPECT(!values.id_alive(id));
}
std::vector<decltype(values)::stable_id> ids_before_clear;
ids_before_clear.reserve(values.size());
for (std::size_t index = 0; index < values.size(); ++index) {
ids_before_clear.push_back(values.id_at(index));
}
values.clear();
for (const auto id : ids_before_clear) {
EXPECT(!values.id_alive(id));
}
EXPECT(!values.id_alive(decltype(values)::invalid_id));
values.push_back(77);
const auto after_clear = values.id_at(0);
EXPECT(values.id_alive(after_clear));
EXPECT(std::find(ids_before_clear.begin(), ids_before_clear.end(), after_clear)
== ids_before_clear.end());
}
void moved_from_sequences_are_reusable_and_assignment_invalidates_proxies() {
using Sequence = uc::AdaptiveSequence<std::uint32_t, true>;
Sequence source({8, 4, 3});
for (std::uint32_t value = 0; value < 64; ++value) {
source.push_back(value);
}
source.force_tiered_mode();
const auto transferred_id = source.id_at(17);
Sequence moved(std::move(source));
EXPECT(moved.size() == 64);
EXPECT(moved.id_alive(transferred_id));
EXPECT(source.empty());
EXPECT(source.capacity() == 0);
EXPECT(source.mode() == uc::StorageMode::vector);
EXPECT(!source.id_alive(transferred_id));
for (std::uint32_t value = 0; value < 40; ++value) {
source.push_back(1'000 + value);
}
EXPECT(source.size() == 40);
EXPECT(source.contains(1'039));
Sequence destination;
destination.push_back(7);
auto stale_after_move_assignment = destination[0];
destination = std::move(moved);
bool move_assignment_invalidated = false;
try {
stale_after_move_assignment = 8;
} catch (const std::logic_error&) {
move_assignment_invalidated = true;
}
EXPECT(move_assignment_invalidated);
EXPECT(destination.id_alive(transferred_id));
EXPECT(moved.empty());
moved.push_back(2'000);
EXPECT(moved.contains(2'000));
Sequence copy_source;
copy_source.push_back(99);
auto stale_after_copy_assignment = destination[0];
destination = copy_source;
bool copy_assignment_invalidated = false;
try {
stale_after_copy_assignment = 9;
} catch (const std::logic_error&) {
copy_assignment_invalidated = true;
}
EXPECT(copy_assignment_invalidated);
EXPECT(destination.size() == 1);
EXPECT(destination.contains(99));
}
void hash_backward_shift_handles_wraparound_and_duplicates() {
using Sequence = uc::AdaptiveSequence<std::uint32_t, true,
uc::ResizePolicy, WraparoundHash>;
Sequence values;
std::vector<Sequence::stable_id> ids;
for (std::uint32_t value = 0; value < 12; ++value) {
values.push_back(value);
ids.push_back(values.id_at(values.size() - 1));
}
values.push_back(5);
const auto duplicate_id = values.id_at(values.size() - 1);
const auto buckets = values.hash_bucket_count();
// Every distinct key starts in the final bucket, so the probe chain wraps
// through bucket zero. Removing a middle key must compact that chain
// without losing later keys or the duplicate list.
values.erase_by_id(ids[3]);
EXPECT(!values.id_alive(ids[3]));
for (std::uint32_t value = 0; value < 12; ++value) {
EXPECT(values.contains(value) == (value != 3));
}
EXPECT(values.find_all(5).size() == 2);
EXPECT(values.id_alive(ids[5]));
EXPECT(values.id_alive(duplicate_id));
values.erase_by_id(ids[5]);
EXPECT(values.contains(5));
EXPECT(values.find_all_ids(5)
== std::vector<Sequence::stable_id>({duplicate_id}));
EXPECT(values.hash_bucket_count() == buckets);
}
void indexed_proxy_detects_structural_invalidation() { void indexed_proxy_detects_structural_invalidation() {
uc::AdaptiveSequence<std::uint32_t, true> values({8, 4, 2}); uc::AdaptiveSequence<std::uint32_t, true> values({8, 4, 2});
values.push_back(10); values.push_back(10);
@@ -278,47 +436,66 @@ void tiered_shape_rebuild_preserves_order() {
EXPECT(values.policy().telemetry().tiered_directory_rebuilds == 2); EXPECT(values.policy().telemetry().tiered_directory_rebuilds == 2);
} }
void adaptation_is_deferred_until_a_safe_boundary() { void capacity_boundaries_control_mode_and_geometry() {
using sequence_type = uc::AdaptiveSequence< uc::ResizePolicyConfig policy_config;
std::uint32_t, false, DeferredTransitionPolicy>; policy_config.minimum_tiered_size = 8;
uc::ResizePolicy policy(policy_config, {4, 4, 3});
uc::AdaptiveSequence<std::uint32_t> values({4, 4, 3}, policy);
sequence_type explicit_maintenance({8, 4, 2}, DeferredTransitionPolicy{}); for (std::uint32_t value = 0; value < 8; ++value) {
for (std::uint32_t value = 0; value < 4; ++value) { values.push_back(value);
explicit_maintenance.push_back(value);
} }
explicit_maintenance.insert(2, 99); EXPECT(values.capacity() == 8);
EXPECT(explicit_maintenance.mode() == uc::StorageMode::vector); EXPECT(values.mode() == uc::StorageMode::vector);
EXPECT(explicit_maintenance.policy().decision_ready());
EXPECT(explicit_maintenance.policy().transitions == 0);
EXPECT(explicit_maintenance.adapt_now());
EXPECT(explicit_maintenance.mode() == uc::StorageMode::tiered);
EXPECT(explicit_maintenance.policy().decisions == 1);
EXPECT(explicit_maintenance.policy().transitions == 1);
EXPECT(explicit_maintenance.policy().last_from == uc::StorageMode::vector);
EXPECT(explicit_maintenance.policy().last_to == uc::StorageMode::tiered);
EXPECT(!explicit_maintenance.adapt_now());
EXPECT(!explicit_maintenance.adapt_now());
EXPECT(explicit_maintenance.policy().decisions == 1);
EXPECT(explicit_maintenance.policy().transitions == 1);
expect_equal(explicit_maintenance,
std::vector<std::uint32_t>({0, 1, 99, 2, 3}));
sequence_type next_mutation({8, 4, 2}, DeferredTransitionPolicy{}); // The ninth element is the next x2 capacity boundary. The representation
for (std::uint32_t value = 0; value < 4; ++value) { // and sqrt(N) leaf are selected as part of that single rebuild.
next_mutation.push_back(value); values.push_back(8);
EXPECT(values.capacity() == 16);
EXPECT(values.mode() == uc::StorageMode::tiered);
EXPECT(values.tiered_config().leaf_capacity == 4);
const auto leaf_at_16 = values.tiered_config().leaf_capacity;
for (std::uint32_t value = 9; value < 16; ++value) {
values.push_back(value);
EXPECT(values.tiered_config().leaf_capacity == leaf_at_16);
} }
next_mutation.insert(1, 77);
EXPECT(next_mutation.mode() == uc::StorageMode::vector);
EXPECT(next_mutation.policy().transitions == 0);
// The pending recommendation is applied before this append; it is not values.push_back(16);
// applied after the preceding, already committed insertion. EXPECT(values.capacity() == 32);
next_mutation.push_back(4); EXPECT(values.mode() == uc::StorageMode::tiered);
EXPECT(next_mutation.mode() == uc::StorageMode::tiered); EXPECT(values.tiered_config().leaf_capacity == 5);
EXPECT(next_mutation.policy().decisions == 1);
EXPECT(next_mutation.policy().transitions == 1); // Shrink is exactly one /2 step at 12.5% occupancy. At N=4 the same
expect_equal(next_mutation, // rebuild crosses the cutoff in the opposite direction.
std::vector<std::uint32_t>({0, 77, 1, 2, 3, 4})); while (values.size() > 4) {
values.erase(values.size() - 1);
}
EXPECT(values.capacity() == 16);
EXPECT(values.mode() == uc::StorageMode::vector);
expect_equal(values, std::vector<std::uint32_t>({0, 1, 2, 3}));
EXPECT(!values.adapt_now());
EXPECT(values.mode() == uc::StorageMode::vector);
}
void calibrated_default_cutoff_is_applied_at_resize() {
uc::AdaptiveSequence<std::uint32_t> values;
for (std::uint32_t value = 0; value < 4'096; ++value) {
values.push_back(value);
}
EXPECT(values.capacity() == 4'096);
EXPECT(values.mode() == uc::StorageMode::vector);
values.push_back(4'096);
EXPECT(values.capacity() == 8'192);
EXPECT(values.mode() == uc::StorageMode::tiered);
EXPECT(values.tiered_config().leaf_capacity == 65);
while (values.size() > 1'024) {
values.erase(values.size() - 1);
}
EXPECT(values.capacity() == 4'096);
EXPECT(values.mode() == uc::StorageMode::vector);
} }
std::optional<uc::AdaptationDecision> shape_decision( std::optional<uc::AdaptationDecision> shape_decision(
@@ -540,69 +717,42 @@ void policy_changes_shape_with_scale_and_locality() {
EXPECT(depth->tiered_config.directory_levels == 3); EXPECT(depth->tiered_config.directory_levels == 3);
} }
void container_applies_same_mode_shape_decision() { void indexed_hash_capacity_tracks_container_capacity() {
uc::AdaptationConfig config; uc::ResizePolicyConfig policy_config;
config.evaluation_interval = 64; policy_config.minimum_tiered_size = 32;
config.minimum_observations = 64; uc::AdaptiveSequence<std::uint32_t, true> values(
config.minimum_residency_operations = 0; {8, 8, 3}, uc::ResizePolicy(policy_config, {8, 8, 3}));
config.minimum_shape_residency_operations = 0;
config.minimum_shape_edit_fraction = 0.0;
config.required_confirmation_windows = 1;
config.minimum_tiered_size = 1;
config.forecast_operations = 1'000'000;
config.tiered_rebuild_safety = 0.0;
config.minimum_shape_improvement = 0.0;
config.vector_move_unit = 1.0;
const uc::TieredConfig initial_shape{1024, 64, 4};
uc::AdaptiveSequence<std::uint32_t> values(
initial_shape, uc::CostModelPolicy(config, initial_shape));
for (std::uint32_t i = 0; i < 10'000; ++i) {
values.push_back(i);
}
values.force_tiered_mode(initial_shape);
values.enable_auto_mode();
for (std::size_t i = 0; auto previous_capacity = values.capacity();
i < 256 && values.policy().telemetry().tiered_rebuilds == 0; ++i) { auto previous_buckets = values.hash_bucket_count();
const auto position = (i * 104'729 + 17) % values.size(); for (std::uint32_t value = 0; value < 257; ++value) {
values.insert(position, 0xabcdefu); values.push_back(value * 16);
values.erase(position); EXPECT(values.contains(value * 16));
if (values.capacity() == previous_capacity) {
EXPECT(values.hash_bucket_count() == previous_buckets);
} else {
EXPECT(values.capacity() == 1
|| values.capacity() == previous_capacity * 2);
EXPECT(values.hash_bucket_count() >= previous_buckets);
previous_capacity = values.capacity();
previous_buckets = values.hash_bucket_count();
}
} }
EXPECT(values.mode() == uc::StorageMode::tiered); // These keys share low hash bits and exercise backward-shift deletion in
EXPECT(values.policy().telemetry().tiered_rebuilds == 1); // the linear-probing table without an intermediate rehash.
EXPECT(values.tiered_config().leaf_capacity <= 128); for (std::uint32_t value = 0; value < 257; value += 2) {
for (std::size_t i = 0; i < values.size(); ++i) { EXPECT(values.erase_one(value * 16));
EXPECT(values[i] == i); }
for (std::uint32_t value = 0; value < 257; ++value) {
EXPECT(values.contains(value * 16) == (value % 2 != 0));
} }
}
void policy_switches_for_sustained_middle_edits() { const auto buckets_before_shrink = values.hash_bucket_count();
uc::TieredConfig tiered{32, 16, 3}; while (values.size() > values.capacity() / 8) {
uc::AdaptationConfig adaptation; values.erase(values.size() - 1);
adaptation.evaluation_interval = 64;
adaptation.minimum_observations = 64;
adaptation.minimum_residency_operations = 0;
adaptation.minimum_tiered_size = 128;
adaptation.forecast_operations = 16'384;
adaptation.vector_to_tiered_safety = 1.0;
// Make the decision boundary deliberately small; this test checks policy
// plumbing, while machine-calibrated constants are validated by benchmarks.
adaptation.vector_move_unit = 0.20;
adaptation.tiered_move_unit = 0.20;
adaptation.tiered_read_base = 2.0;
uc::AdaptiveSequence<std::uint32_t> values(
tiered, uc::CostModelPolicy(adaptation, tiered));
for (std::uint32_t i = 0; i < 512; ++i) {
values.push_back(i);
} }
values.enable_auto_mode(); EXPECT(values.hash_bucket_count() <= buckets_before_shrink);
for (std::size_t i = 0; i < 256 && values.mode() != uc::StorageMode::tiered; ++i) {
values.insert(values.size() / 2, 7);
values.erase(values.size() / 2);
}
EXPECT(values.mode() == uc::StorageMode::tiered);
} }
} // namespace } // namespace
@@ -611,17 +761,20 @@ int main() {
ring_block_offsets(); ring_block_offsets();
randomized_differential(); randomized_differential();
indexed_duplicates_and_ids(); indexed_duplicates_and_ids();
indexed_ids_reuse_slots_without_reviving_stale_handles();
moved_from_sequences_are_reusable_and_assignment_invalidates_proxies();
hash_backward_shift_handles_wraparound_and_duplicates();
indexed_proxy_detects_structural_invalidation(); indexed_proxy_detects_structural_invalidation();
iterator_and_contiguous_contract(); iterator_and_contiguous_contract();
non_trivial_values(); non_trivial_values();
tiered_shape_rebuild_preserves_order(); tiered_shape_rebuild_preserves_order();
adaptation_is_deferred_until_a_safe_boundary(); capacity_boundaries_control_mode_and_geometry();
calibrated_default_cutoff_is_applied_at_resize();
forecast_horizon_is_bounded_by_observed_evidence(); forecast_horizon_is_bounded_by_observed_evidence();
confirmation_requires_consecutive_supporting_windows(); confirmation_requires_consecutive_supporting_windows();
stationary_phases_do_not_thrash(); stationary_phases_do_not_thrash();
policy_changes_shape_with_scale_and_locality(); policy_changes_shape_with_scale_and_locality();
container_applies_same_mode_shape_decision(); indexed_hash_capacity_tracks_container_capacity();
policy_switches_for_sustained_middle_edits();
if (failures != 0) { if (failures != 0) {
std::cerr << failures << " test assertion(s) failed\n"; std::cerr << failures << " test assertion(s) failed\n";
+1 -1
View File
@@ -6,7 +6,7 @@ param(
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
$build = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot "..\out\build\msvc-$Profile")) $build = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot "..\out\build\msvc-$Profile"))
$targets = @('uc_demo', 'uc_tests', 'uc_bench') $targets = @('uc_demo', 'uc_tests', 'uc_bench', 'uc_focused_bench')
function Require-Flag( function Require-Flag(
[string]$Command, [string]$Command,