WIP article for alt article

This commit is contained in:
2026-08-18 13:14:15 +03:00
parent 74e3c9d6d3
commit 9f3b01f445
+53 -2
View File
@@ -3,6 +3,7 @@
Ориентир на 700 слов, 9к символов Ориентир на 700 слов, 9к символов
(!!!)/(???) - дополнить/уточнить (!!!)/(???) - дополнить/уточнить
всё ещё куда-то надо включить "хэш клёвый, но медленный, вот тесты" всё ещё куда-то надо включить "хэш клёвый, но медленный, вот тесты"
Проверить, чтобы всё, что нужно в `это` попало
## 1. Зачем нужны set-строки ## 1. Зачем нужны set-строки
@@ -14,11 +15,11 @@
set-строка формируется следующим образом: set-строка формируется следующим образом:
1. Список символов формируется автодепами `rpm-build`. 1. Список символов формируется автодепами `rpm-build`.
2. По количеству `Provides` символов вычисляется `bpp` (количество бит до которых обрезается хэш символа при формировании строки). (!!!) 2. По количеству `Provides` символов (`cnt`) вычисляется `bpp` - количество бит до которых обрезается хэш символа при формировании строки. (!!!)
3. Для каждого символа считается хэш-функция (используется Jenkins OAAT), хэш обрезается до `bpp` бит. 3. Для каждого символа считается хэш-функция (используется Jenkins OAAT), хэш обрезается до `bpp` бит.
4. Массив хэшей сортируются, повторы удаляются. 4. Массив хэшей сортируются, повторы удаляются.
5. Абсолютные значения заменяются дельтами между значениями 5. Абсолютные значения заменяются дельтами между значениями
6. Для кодировки Golomb-Rice вычисляется параметр `Mshift`. (!!!) bpp - log2i(cnt) - 1 6. Для кодировки Golomb-Rice вычисляется параметр `Mshift=bpp - log2(cnt) - 1`.
7. Массив дельт сжимаются кодировкой Golomb-Rice. 7. Массив дельт сжимаются кодировкой Golomb-Rice.
8. Битовый поток преобразуется в Base62-строку. 8. Битовый поток преобразуется в Base62-строку.
@@ -83,8 +84,58 @@ set-строка
### 3.1. Слитый декодер ### 3.1. Слитый декодер
Вместо описанной выше последовательности декодирования set-строк применяется функция, объединяющая этапы `base62` и `golomb`.
`decode_base62_golomb()` - оптимизированная версия стадий `decode_base62` и `decode_golomb`. Функция считывает сразу по два байта, с помощью пре-compiled таблицы преобразует их в битовую последовательность, набирая блоки до 24 бит, после декодирует по `Rice-Golomb`.
Благодаря такому подходу удаётся ускорить работу алгоритма на сравнении строк в ~2 раза. (!!!)
### 3.2. Кэширование `Provides` set-строк ### 3.2. Кэширование `Provides` set-строк
При передачи первого параметра (`const char *set1`) в функцию сравнения set-строк (`rpmsetcmp()`), set-строка кэшируется.
Используется простой LRU кэш (массив размером `256`), который сохраняет fingerprint оригинальной set-строки, саму set-строку и декодированный массив хэшей.
При cache_hit элемент смещается на первую позицию, а при первом попадании попадает на позицию `min(243, len(cache))`.
Очевидным недостатком такого подхода является:
1. Малый размер кэша
при увеличении размера кэша до 512 элементов, производительность увеличилась на X% (!!!)
2. Затраты на `realloc` при cache_hit
из-за хранения элементов кэша как массив (а не списком, например), после каждого попадания кэша приходится смещать до 255 записей.
### 3.3. Быстрое сравнение включения множеств символов ### 3.3. Быстрое сравнение включения множеств символов
После получения отсортированных и усечённых до одинакового `bpp` масивов хэшей, необходимо проверить включение множеств. Отметим, что множество `req` символов будет, как правило, разреженным относительно множества `prov` символов.
`lib/set.c` делает это с помощью макроса `IFLT4`(`IFLT8`).
Данный макрос отвечает за быстрые прыжки на 4(8) элементов массива, и последующее уточнение на 2(4), 1(2) и 0(1) элемента, пока не найдём позицию, где `hash_arr1[i] <= hash_arr2[j] > hash_arr1[i+1]`.
Макрос `IFLT8` с первоначальным прыжком на 8 элементов выбирается при `len(hash_arr1) >= 16 * len(hash_arr2)`. В остальных случаях используется макрос `IFLT4`.
Из недостатков данного способа выделяется фиксированная длина прыжка, которую имеет смысл увеличивать пропорционально `len(hash_arr1) / len(hash_arr2)`.
## 4. Дальнейшие оптимизации ## 4. Дальнейшие оптимизации
(???) Говорить ли про python-реализацию вовсе
Текущая реализация `lib/set.c` трудночитаемая и труднопонимаемая, а также не содержит некоторых оптимизаций, которые могли бы сильнее ускорить работу библиотеки.
В связи с этим, было принято решение о реимплементации кода с сохранением совместимости к текущему формату set-строк.
### 4.1. Слитый энкодер
В прошлом разделе говорилось о ускорении работы функции `rpmsetcmp()`, однако для части кода отвечающей за создание set-строк как таковых оптимизаций не существует.
Поэтому энкодер в новой версии пропускает стадию создания битового масива и напрямую декодирует base62 строки в массив delta.
### 4.2. Общая память под строки
Для улучшения encode составляющей также была изменена работа с памятью под символы. В новой версии вместо множества указателей на строки, под каждый из которых требуется свой `malloc`, введён единый указатель, в котором хранятся все символы последовательно, а индекс начала каждого из символа хранится отдельно.
Это позволяет снизить часть расходов на `malloc`.
### 4.3. Radix-sort
### 4.3. Изменённый кэш