article.tex

This commit is contained in:
2026-09-10 19:01:37 +03:00
parent 1cca8e5c88
commit fe2edcea5c
+100 -114
View File
@@ -1,43 +1,56 @@
% Подключаемый фрагмент по образцу abstract.tex. \author{Дмитрий Гудов}
% Авторские данные, аннотация и ключевые слова в исходном тексте не заданы.
% Город
\city{Москва}
% Организация
\affiliation{ALT Linux}
% Название проекта, которому посвящён доклад (необязательно,
% но желательно).
% \projecttitle{set-строки в rpm}
% Сайт(ы) проекта
\projecturl{\url{https://github.com/kr0sh512/alt-rpm-set-version}}
\title{Исследование и улучшение механизма работы \texttt{set}-строк в ALT RPM} \title{Исследование и улучшение механизма работы \texttt{set}-строк в ALT RPM}
\maketitle \maketitle
\begingroup \begin{abstract}
\setlength{\emergencystretch}{3em} В ALT RPM версии зависимостей \texttt{set:} кодируют множества ELF-символов и
позволяют проверять совместимость библиотек точнее, чем по одному SONAME.
В работе разобраны формат строк и оптимизации исходного \texttt{lib/set.c},
описана совместимая реимплементация \texttt{set9.c} и приведены результаты её
проверки на метаданных Sisyphus и в симуляциях APT. Отдельно рассмотрены
несовместимые альтернативные форматы и замена хэш-функции.
\end{abstract}
Ориентир на 700 слов, 9к символов \keywords{ALT RPM, ELF-символы, set:version, Golomb--Rice}
(!!!)/(???) - дополнить/уточнить
всё ещё куда-то надо включить "хэш клёвый, но медленный, вот тесты"
Проверить, чтобы всё, что нужно в \texttt{это} попало
\section*{1. Зачем нужны set-строки} \section*{1. Зачем нужны set-строки}
Обычная зависимость от версии библиотеки не гарантирует, что в ней остались все нужные программе символы: символ можно удалить, не сменив SONAME, а одинаковые SONAME могут скрывать разные наборы экспортов. Поэтому ALT RPM использует версии зависимостей вида \texttt{set:<encoded-set>}. Для \texttt{Provides} такая строка описывает символы, предоставляемые библиотекой, а для \texttt{Requires} - символы, которые конкретный потребитель требует от неё. В такой конфигурации сравниваются не номера версий, а включение множеств: все хэши символов из \texttt{Requires} должны присутствовать в \texttt{Provides}. Обычная зависимость от версии библиотеки не гарантирует, что в ней остались все нужные программе символы: символ можно удалить, не сменив SONAME, а одинаковые SONAME могут скрывать разные наборы экспортов. Поэтому ALT RPM использует версии зависимостей вида \texttt{set:<encoded-set>}. Для \texttt{Provides} такая строка описывает символы, предоставляемые библиотекой, а для \texttt{Requires} --- символы, которые конкретный потребитель требует от неё. В такой конфигурации сравниваются не номера версий, а включение множеств: все хэши символов из \texttt{Requires} должны присутствовать в \texttt{Provides}.
Гарантия вероятностная, поскольку вместо полноценных имён символов хранятся усечённые хэши, но ответ "символ отсутствует", когда он есть мы не получим
Гарантия вероятностная, поскольку вместо полных имён символов хранятся усечённые хэши. Ложное отрицание невозможно: совпадающее имя всегда даёт то же хэш-значение. Возможны ложные положительные результаты, если разные имена имеют одинаковый усечённый хэш.
\section*{2. Структура set-строки} \section*{2. Структура set-строки}
set-строка формируется следующим образом: Set-строка формируется следующим образом:
\begin{enumerate} \begin{enumerate}
\item Список символов формируется автодепами \texttt{rpm-build}. \item Список символов формируется автодепами \texttt{rpm-build}. Для \texttt{Provides} это отфильтрованные экспортируемые базовые имена ELF-символов; для \texttt{Requires} \texttt{ldd --bindings} связывает сильные неопределённые символы с конкретной библиотекой.
\item По количеству \texttt{Provides} символов (\texttt{cnt}) вычисляется \texttt{bpp} - количество бит до которых обрезается хэш символа при формировании строки. (!!!) \item Для каждого имени вычисляется Jenkins OAAT; при формировании строки оставляются младшие \texttt{bpp} бит хэша. Допустимый диапазон \texttt{bpp} --- от 10 до 32.
\item Для каждого символа считается хэш-функция (используется Jenkins OAAT), хэш обрезается до \texttt{bpp} бит. \item Массив хэшей сортируется, повторы удаляются, а абсолютные значения заменяются дельтами между соседними значениями.
\item Массив хэшей сортируются, повторы удаляются. \item Для кодирования Golomb--Rice вычисляется параметр \texttt{Mshift = bpp - log2(cnt) - 1}; затем он ограничивается диапазоном 7--31 так, чтобы \texttt{Mshift < bpp}.
\item Абсолютные значения заменяются дельтами между значениями \item Массив дельт сжимается кодировкой Golomb--Rice, а битовый поток преобразуется в Base62-строку.
\item Для кодировки Golomb-Rice вычисляется параметр \texttt{Mshift=bpp - log2(cnt) - 1}.
\item Массив дельт сжимаются кодировкой Golomb-Rice.
\item Битовый поток преобразуется в Base62-строку.
\end{enumerate} \end{enumerate}
Примечание: при формировании set-строки для \texttt{req} символов, \texttt{bpp} вычисляется по количеству \texttt{prov} символов в актуальной (???) версии библиотеки для избежания сильного усечения хэшей при небольшом количестве требуемых символов. При формировании строки \texttt{Requires} точность \texttt{bpp} выбирается по числу символов соответствующей предоставляющей библиотеки, а не по числу требуемых символов. Это сохраняет одинаковую точность представления для обеих сторон зависимости и уменьшает риск коллизий в малом множестве \texttt{Requires}.
Итоговая строка выглядит следующим образом: Итоговая строка имеет вид:
\begin{verbatim} \begin{verbatim}
set:<bpp_char><Mshift_char><base62 строка> set:<bpp_char><Mshift_char><base62-полезная нагрузка>
\end{verbatim} \end{verbatim}
\begin{verbatim} \begin{verbatim}
@@ -51,125 +64,82 @@ Mshift_char = Mshift - 7 + 'a'
массив строк массив строк
| (Jenkins OAAT) | (Jenkins OAAT)
v v
массив усечённых хэшей массив усечённых хэшей -- сортировка и удаление повторов
| (qsort) | (разности соседних значений)
v v
отсортированный массив хэшей массив дельт
| (вычисление разницы между элементами) | (преобразование Golomb--Rice)
v v
массив delta битовый поток
| (Rice-Golomb преобразование) | (Base62)
v
битовый массив
| (base62 преобразование)
v v
set-строка set-строка
\end{verbatim} \end{verbatim}
\subsection*{Механизм сравнения set-строк} \subsection*{Механизм сравнения set-строк}
Для проверки включения множества символов \texttt{req} в множество \texttt{prov} необходимо выполнить обратное декодирование следующим образом: Для проверки включения множества символов \texttt{Requires} в множество \texttt{Provides} выполняется обратное декодирование:
\begin{verbatim} \begin{verbatim}
set-строка set-строка
| (обратное base62 преобразование) | (обратное Base62-преобразование)
v v
битовый массив битовый поток
| (обратное Rice-Golomb преобразование) | (обратное преобразование Golomb--Rice)
v v
массив delta массив дельт
| (вычисление изначальных значений) | (накопление дельт)
v v
массив усечённых хэшей (отсортированный) отсортированный массив усечённых хэшей
\end{verbatim} \end{verbatim}
Значения хэшей в массивах приводятся к минимальному \texttt{bpp} из двух set-строк (сохраняется отсортированность и дистинктивность(???)). Если точности строк различаются, значения приводятся к минимальному \texttt{bpp} из двух строк. После проекции сохраняются сортировка и уникальность: части массива по разные стороны удаляемого старшего бита сливаются с удалением повторов. Затем включение одного отсортированного множества усечённых хэш-значений в другое проверяется линейным проходом с пропусками.
После получения отсортированных массивов хэшей из set-строк, включение символов (а точнее их усечённых хэш-значений) одного множества во второе проверить не составляет труда.
\section*{3. Практическая реализация} \section*{3. Практическая реализация}
Несмотря на описанную структуру set-строки, на практике в текущем \texttt{lib/set.c} применяется множество оптимизаций и улучшений, направленных на ускорение работы \texttt{rpmsetcmp(const char *set1, const char *set2)} (функции, выдающей результат включения множеств). Рассмотрим основные из них. Несмотря на описанную структуру set-строки, в текущем \texttt{lib/set.c} применяется множество оптимизаций, направленных на ускорение \texttt{rpmsetcmp(const char *set1, const char *set2)}. В рабочем вызове первым аргументом передаётся \texttt{Provides}, вторым --- \texttt{Requires}; результат \texttt{1} означает строгое включение первого множества во второе, \texttt{0} --- равенство, а \texttt{-1} и \texttt{-2} --- обратное включение и несравнимость соответственно.
\subsection*{3.1. Слитый декодер} \subsection*{3.1. Слитый декодер}
Вместо описанной выше последовательности декодирования set-строк применяется функция, объединяющая этапы \texttt{base62} и \texttt{golomb}. Вместо последовательного декодирования Base62 и Golomb--Rice применяется функция \texttt{decode\_base62\_golomb()}, объединяющая оба этапа. Она считывает Base62-полезную нагрузку парами символов, с помощью заранее вычисленной таблицы получает биты, накапливает блоки до 24 бит и сразу декодирует значения Golomb--Rice. После этого восстанавливаются абсолютные значения из дельт.
\texttt{decode\_base62\_golomb()} - оптимизированная версия стадий \texttt{decode\_base62} и \texttt{decode\_golomb}. Функция считывает сразу по два байта, с помощью пре-compiled таблицы преобразует их в битовую последовательность, набирая блоки до 24 бит, после декодирует по \texttt{Rice-Golomb}. Роль этой оптимизации подтверждается отдельной контрольной серией: вариант без оптимизированного декодера расходовал в APT-симуляциях в 1,47--2,82 раза больше суммарного процессорного времени, чем исходный \texttt{set.c}.
Благодаря такому подходу удаётся ускорить работу алгоритма на сравнении строк в \textasciitilde{}2 раза. (!!!)
\subsection*{3.2. Кэширование \texttt{Provides} set-строк} \subsection*{3.2. Кэширование \texttt{Provides} set-строк}
При передачи первого параметра (\texttt{const char *set1}) в функцию сравнения set-строк (\texttt{rpmsetcmp()}), set-строка кэшируется. При передаче первого параметра в \texttt{rpmsetcmp()} его строка кэшируется. Исходная реализация использует массив из 256 записей, содержащих короткий отпечаток, исходную строку и декодированный массив хэшей; совпадение отпечатка подтверждается полным сравнением строки. При попадании запись перемещается в начало двумя вызовами \texttt{memmove()}; при заполнении кэша последняя запись освобождается, а новая помещается в позицию 243. Поэтому этот кэш близок к LRU, но не является строгим LRU.
Используется простой LRU кэш (массив размером \texttt{256}), который сохраняет fingerprint оригинальной set-строки, саму set-строку и декодированный массив хэшей.
При cache\_hit элемент смещается на первую позицию, а при первом попадании попадает на позицию \texttt{min(243, len(cache))}. Кэшируется только первый операнд, а второй декодируется при каждом сравнении. Следовательно, попадание в кэш устраняет не всё сравнение, а только повторное декодирование \texttt{Provides}; нормализация \texttt{bpp} также выполняется заново.
Очевидным недостатком такого подхода является:
\begin{enumerate}
\item Малый размер кэша
при увеличении размера кэша до 512 элементов, производительность увеличилась на X\% (!!!)
\item Затраты на \texttt{realloc} при cache\_hit
из-за хранения элементов кэша как массив (а не списком, например), после каждого попадания кэша приходится смещать до 255 записей.
\end{enumerate}
\subsection*{3.3. Быстрое сравнение включения множеств символов} \subsection*{3.3. Быстрое сравнение включения множеств символов}
После получения отсортированных и усечённых до одинакового \texttt{bpp} масивов хэшей, необходимо проверить включение множеств. Отметим, что множество \texttt{req} символов будет, как правило, разреженным относительно множества \texttt{prov} символов. После получения отсортированных и приведённых к одинаковому \texttt{bpp} массивов хэшей необходимо проверить включение множеств. Как правило, множество \texttt{Requires} разрежено относительно \texttt{Provides}. Для этого \texttt{lib/set.c} использует макросы \texttt{IFLT4} и \texttt{IFLT8}: они делают прыжки на 4 или 8 элементов и уточняют позицию меньшими шагами. Вариант \texttt{IFLT8} выбирается при \texttt{len(Provides) >= 16 * len(Requires)}, в остальных случаях используется \texttt{IFLT4}.
\texttt{lib/set.c} делает это с помощью макроса \texttt{IFLT4}(\texttt{IFLT8}). Фиксированные шаги являются компромиссом между простотой и числом сравнений. Они хорошо работают для разреженных требований, но не учитывают точное отношение размеров множеств.
Данный макрос отвечает за быстрые прыжки на 4(8) элементов массива, и последующее уточнение на 2(4), 1(2) и 0(1) элемента, пока не найдём позицию, где \texttt{hash\_arr1[i] <= hash\_arr2[j] > hash\_arr1[i+1]}. \section*{4. Совместимая реимплементация}
Макрос \texttt{IFLT8} с первоначальным прыжком на 8 элементов выбирается при \texttt{len(hash\_arr1) >= 16 * len(hash\_arr2)}. В остальных случаях используется макрос \texttt{IFLT4}. Для упрощения сопровождения была создана совместимая с форматом и публичным API реализация \texttt{set9.c}. В \texttt{set9.c} были реализованы следующие изменения.
Из недостатков данного способа выделяется фиксированная длина прыжка, которую имеет смысл увеличивать пропорционально \texttt{len(hash\_arr1) / len(hash\_arr2)}. \subsection*{4.1. Потоковое кодирование и хранение символов}
\section*{4. Дальнейшие оптимизации} Энкодер \texttt{set9.c} передаёт значения по цепочке «хэш --- дельта --- Golomb--Rice --- Base62» без промежуточных массивов битов и дельт. Вместо отдельных выделений памяти под каждое имя используется растущая строковая арена, а в массиве символов хранятся смещения. Это позволет существенно уменьшить число вызовов \texttt{malloc()}.
(???) Говорить ли про python-реализацию вовсе \subsection*{4.2. Сортировка}
Текущая реализация \texttt{lib/set.c} трудночитаемая и труднопонимаемая, а также не содержит некоторых оптимизаций, которые могли бы сильнее ускорить работу библиотеки. Для наборов меньше 128 значений сохраняется \texttt{qsort()}, а для больших используется стабильная побайтовая LSD radix sort. Число проходов равно \texttt{ceil(bpp/8)} и не превышает четырёх; дополнительная память имеет размер \texttt{O(n + 256)}.
В связи с этим, было принято решение о реимплементации кода с сохранением совместимости к текущему формату set-строк. \subsection*{4.3. Изменённый кэш и декодер}
\subsection*{4.1. Слитый энкодер} В новой реализации используются два независимых кэша по 512 записей --- по одному для каждого операнда. Поиск выполняется через хэш-бакеты, а двусвязный LRU-список перемещает запись за \texttt{O(1)}. Ключ включает исходную строку и целевой \texttt{bpp}, поэтому результат нормализации можно повторно использовать.
В прошлом разделе говорилось о ускорении работы функции \texttt{rpmsetcmp()}, однако для части кода отвечающей за создание set-строк как таковых оптимизаций не существует. Потоковый декодер использует 64-битный аккумулятор и более простую таблицу Base62. Для сравнения равных по размеру множеств применяется \texttt{memcmp()}, а для неравных проверяется только потенциальное включение меньшего в большее; разреженный случай использует монотонный поиск с шагом, зависящим от отношения размеров.
Поэтому энкодер в новой версии пропускает стадию создания битового масива и напрямую декодирует base62 строки в массив delta.
\subsection*{4.2. Общая память под строки} \subsection*{4.4. Проверка корректности и производительности}
Для улучшения encode составляющей также была изменена работа с памятью под символы. В новой версии вместо множества указателей на строки, под каждый из которых требуется свой \texttt{malloc}, введён единый указатель, в котором хранятся все символы последовательно, а индекс начала каждого из символа хранится отдельно. \texttt{set9.c} был собран с \texttt{-Wall -Wextra -Werror}; встроенный \texttt{SELF\_TEST} успешно проверил хэширование, сортировку, кодирование и декодирование, метаданные, понижение \texttt{bpp}, включение, кэш, построитель и API. На снимке Sisyphus для \texttt{x86\_64/noarch} от 21 августа 2026 года проверены 68\,494 пары \texttt{set:} с одинаковым capability: 232 пары равны, в 68\,260 случаях \texttt{Provides} строго содержит \texttt{Requires}, а две несравнимые пары \texttt{python3(ast)} дополнительно имеют обычный unversioned \texttt{Provides}. С учётом 661 такого unversioned \texttt{Provides} все 69\,153 set-требования снимка удовлетворяются.
Это позволяет снизить часть расходов на \texttt{malloc}. Для производительности сравнивались две локальные сборки \texttt{librpm}: исходная реализация и версия с описанными изменениями. Замеры выполнялись на одинаковых симуляционных командах \texttt{apt-get}. В таблице приведены средние процессорные времена. Коэффициент равен отношению времени исходной реализации к времени изменённой: значение больше единицы означает ускорение, меньше единицы --- замедление.
\subsection*{4.3. Radix sort}
Вместо \texttt{qsort} при сортировке хэшей символов теперь используется \texttt{radix sort} на количестве элементов массива \textgreater{}128. (при \textless{}=128 остаётся \texttt{qsort})
\subsection*{4.4. Изменённый кэш}
Кэш претерпел множество изменений, т.к. давал сильный прирост в скорости (!!!)
\begin{enumerate}
\item Изменён размер до 512 значений.
\item Добавлен кэш также и для \texttt{req} set-строк. Теперь порядок аргументов не имеет значения для производительности.
\item Кэш теперь строится на списках, а не на массиве, благодаря чему более нет затрат на \texttt{realloc} при cache hit.
\end{enumerate}
\subsection*{4.5. Изменённый декодер}
Оставляя изначальную идею слитого декодера, была написана его реимлементация, использующая более простую логику, но использующая 64-битные блоки, а также упрощённую precompiled таблицу. (???) если есть результаты ускорения, сюда надо
\subsection*{4.6. Тесты производительности}
Для проверки производительности сравнивались две локальные сборки \texttt{librpm}: исходная реализация и версия с описанными изменениями. Замеры выполнялись на одинаковых симуляционных командах \texttt{apt-get}.
Коэффициент считается как отношение времени исходной реализации к времени изменённой. Значение больше \texttt{1} - ускорение, меньше \texttt{1} - замедление.
\begin{center} \begin{center}
\begingroup \begingroup
@@ -177,41 +147,57 @@ set-строка
\setlength{\tabcolsep}{2pt} \setlength{\tabcolsep}{2pt}
\begin{tabular}{@{}lrrrrrr@{}} \begin{tabular}{@{}lrrrrrr@{}}
\hline \hline
Команда & \shortstack{\texttt{set.c}\\user, с} & \shortstack{\texttt{set.c}\\system, с} & \shortstack{\texttt{set9.c}\\user, с} & \shortstack{\texttt{set9.c}\\system, с} & \shortstack{Ускорение\\по user} & \shortstack{Ускорение\\по user+system} \\ Команда & \shortstack{\texttt{set.c}\\user, с} & \shortstack{\texttt{set.c}\\system, с} & \shortstack{\texttt{set9.c}\\user, с} & \shortstack{\texttt{set9.c}\\system, с} & \shortstack{Коэффициент\\по user} & \shortstack{Коэффициент\\по user+system} \\
\hline \hline
\texttt{-s check} & 0.453 & 0.033 & 0.503 & 0.040 & 0.901× & 0.896× \\ \texttt{-s check} & 0.453 & 0.033 & 0.503 & 0.040 & 0.901$\times$ & 0.896$\times$ \\
\texttt{-s autoremove} & 0.763 & 0.040 & 0.810 & 0.040 & 0.942× & 0.945× \\ \texttt{-s autoremove} & 0.763 & 0.040 & 0.810 & 0.040 & 0.942$\times$ & 0.945$\times$ \\
\texttt{-s install rpm-build} & 1.210 & 0.050 & 1.283 & 0.050 & 0.943× & 0.945× \\ \texttt{-s install rpm-build} & 1.210 & 0.050 & 1.283 & 0.050 & 0.943$\times$ & 0.945$\times$ \\
\texttt{-s install openuds-server} & 3.747 & 0.080 & 3.897 & 0.083 & 0.962× & 0.961× \\ \texttt{-s install openuds-server} & 3.747 & 0.080 & 3.897 & 0.083 & 0.962$\times$ & 0.961$\times$ \\
\texttt{-s install password-store} & 1.233 & 0.050 & 1.310 & 0.050 & 0.941× & 0.944× \\ \texttt{-s install password-store} & 1.233 & 0.050 & 1.310 & 0.050 & 0.941$\times$ & 0.944$\times$ \\
\hline \hline
\end{tabular} \end{tabular}
\endgroup \endgroup
\end{center} \end{center}
Во всех пяти завершённых сценариях \texttt{set9.c} медленнее исходной реализации: суммарное процессорное время увеличилось на 4,0--11,6\%. Таким образом, упрощённый декодер с улучшениями сортировки и кэша не компенсировали стоимость более сложного декодера в данной APT-нагрузке. Однако, данная реализация остаётся значительно быстрее работы оригинального \texttt{set.c} с декодированием "в лоб".
\section*{5. Прочие исследования} \section*{5. Прочие исследования}
В данной главе собраны исследования, которые не вошли непосредственно в реализацию новой версии, однако важны своими идеями и результатами. В данной главе собраны исследования, которые не вошли непосредственно в совместимую реализацию, однако важны своими идеями и результатами. Форматы этого раздела несовместимы с существующими \texttt{set:}-строками и требуют регенерации метаданных.
\subsection*{5.1. Реализация без промежуточной кодировки хэшей} \subsection*{5.1. Хранение хэшей без Golomb--Rice}
Первое направление - отказаться от \texttt{delta} и \texttt{Golomb-Rice} и хранить отсортированные усечённые хэши почти напрямую. Заметная часть времени при сравнении тратится на декодирование set-строки. Если сделать строку дешевле в декодировании, можно получить выигрыш на холодном сравнении и проигрыш в длине строки. Первое направление --- отказаться от дельт и Golomb--Rice и хранить отсортированные усечённые хэши почти напрямую в Base64. Это уменьшает стоимость декодирования, но увеличивает строку. В микротесте с 1000 предоставляемыми и 500 требуемыми символами при \texttt{bpp=32} длина строки выросла с 3994 до 5340 символов. Построение строки ускорилось: \texttt{set\_fini} занял 105,09 вместо 147,45 мкс. Холодное сравнение заняло 113,04 вместо 129,67 мкс (0,87$\times$ времени \texttt{set9}), а прогретое --- 1,04 вместо 2,99 мкс (0,35$\times$).
(!!!) Результаты теста Однако результат зависит от нагрузки: при одном требуемом символе прогретое сравнение прямого формата заняло 1,02$\times$ времени \texttt{set9}.
Также было рассмотрено исользование \texttt{Roaring Bitmap}, однако его эффективность достигается для плотных множеств, что не актуально при работе с хэш-функциями. Также был рассмотрен Roaring Bitmap. В проверенном прототипе на разреженных усечённых хэшах он дал более длинные строки и более медленное сравнение: строка выросла до 19\,924 символов, холодное сравнение стало в 4,50 раза, а прогретое --- в 76,14 раза медленнее \texttt{set9}. Сжатие zstd сократило строку до 14\,126 символов, но холодное и прогретое сравнения остались в 4,96 и 101,86 раза медленнее.
\subsection*{5.2. Тестирование коллизий хэш-функций} \subsection*{5.2. Тестирование хэш-функций}
Отдельно проверялась идея заменить \texttt{Jenkins OAAT} на более современную хэш-функцию. По скорости на длинных строках Jenkins действительно проигрывает (!!!) Отдельно проверялась идея заменить Jenkins OAAT на CityHash32 или XXH32. На фиксированных случайных байтовых строках, медиана трёх запусков, Jenkins уступал по скорости: для длины 16 байт получены 22,330 нс/хэш против 10,402 у CityHash32 и 7,792 у XXH32; для 1024 байт --- 1796,485 против 232,598 и 184,234 нс/хэш соответственно.
Однако при тестировании коллизий оказалость, что все три функции на вероятностной побитовой карте показывают близкие к "идеальным" результаты. (!!!) Однако скорость отдельной хэш-функции не описывает её качества на данных,
характерных для \texttt{set:}-строк. Поэтому для Jenkins OAAT, XXH64 и t1ha2
была измерена вероятность единицы в каждом из младших 32 битов хэша на корпусе
из 577\,509 уникальных C++ ABI-символов, извлечённых из 259 ELF-файлов пяти
пакетов ALT p11. Корпус намеренно содержит близкие по префиксу имена: 99,60\%
символов имеют с другим символом общий префикс длиной не менее 12 знаков, а
95,05\% --- не менее 24 знаков. Среднее отклонение $P(\text{bit}=1)$ от
$0{,}5$ равно 0,0475 процентного пункта для Jenkins OAAT, 0,0431 для XXH64 и
0,0615 для t1ha2; максимальное отклонение одного бита --- 0,167, 0,122 и
0,188 процентного пункта соответственно. Следовательно, все три функции
достаточно равномерно смешивают проверенный корпус; только по этой метрике
замена Jenkins OAAT не получает убедительного обоснования.
Следовательно, в проверенной метрике устойчивого преимущества замены Jenkins не обнаружено. Кроме того, новая хэш-функция изменила бы все значения в wire-format и потребовала бы новой версии формата и пересоздания репозиторных метаданных.
\section*{6. Итоги} \section*{6. Итоги}
(нужны ли..) Работа позволила восстановить назначение и инварианты \texttt{set:}-формата, описать пути исходного \texttt{lib/set.c} и подготовить совместимую реализацию \texttt{set9.c}. Проверка на Sisyphus и \texttt{SELF\_TEST} подтверждают корректную обработку исследованного корпуса и публичного API. Вместе с тем APT-измерения показали отрицательный результат по производительности: более читаемая и структурированная реализация не превзошла специализированный декодер исходного кода.
Эксперименты с прямым хранением хэшей, Roaring Bitmap и альтернативными хэш-функциями уточнили границы дальнейшей оптимизации.
\endgroup
%%% Local Variables: %%% Local Variables:
%%% mode: latex %%% mode: latex