Какова гарантия того, что математические функции glibc согласованы в разных версиях?

Вопрос или проблема

Я работаю с программой моделирования на основе метода Монте-Карло для физики частиц и пытаюсь понять некоторую случайность, которую наблюдаю. Принцип этой программы заключается в том, что она вводит частицу в некоторую среду, а затем использует генератор случайных чисел, чтобы решить, какие типы взаимодействий она имеет. Это позволяет создать множество возможных путей для частицы. Как пользователь, я могу указать начальные значения для RNG, чтобы обеспечить воспроизводимость в дальнейшем.

Недавно я наткнулся на нечто странное. Вычислительный кластер, на котором я работаю, был обновлен до новой версии ОС. Все файлы были сохранены, как и бинарный файл программы моделирования. Однако симуляция больше не производит те же результаты, несмотря на использование идентичных начальных значений, как и раньше. Таким образом, как-то та же бинарная программа, на том же оборудовании, но с другой версией Linux, производит другую последовательность взаимодействий.

Изучив документацию фреймворка симуляции, я обнаружил, что используемый внутри RNG — это генератор RANMAR 1. Это должен быть полностью портируемый RNG, следовательно, не зависящий от используемой системы. Следовательно, он не должен быть источником разных выходных данных. После разговора с разработчиками они пришли к выводу, что разница, вероятно, будет заключаться в том, как выполняются некоторые математические операции (например, тригонометрические функции). В конце концов, бинарный файл динамически связывается с основной библиотекой, и она действительно была изменена во время обновления ОС. В частности, версия glibc была обновлена с 2.17 до 2.34.

Я пытался найти в Интернете, может ли это действительно иметь значение для функций, включенных в libc и libm, но не смог найти какого-либо окончательного ответа. Я нашел некоторые упоминания о различиях в более новых версиях, но это больше похоже на ошибку, которая позднее была исправлена. Поэтому мой вопрос: есть ли гарантия, что математические функции в более новых версиях glibc будут давать точно такие же результаты? И, возможно, связанный вопрос, есть ли простой способ проверить, что изменилось между двумя версиями библиотеки?

Я не думаю, что существует какая-то гарантия, кроме того, что разработчики стремятся предоставлять реализации, асимптотически стремящиеся к тому, что указано в стандартах (см. ссылки в разделе “Математика” руководства). Например, changelog для версии 2.41 упоминает

Оптимизированные и правильно округленные функции exp10m1f, exp2m1f, expm1f, log10f,
log2p1f, log1pf, log10p1f, cbrtf, erff, erfcf, lgammaf, tgammaf,
tanf, acosf, acoshf, asinf, asinhf, atanf, atan2f, atanhf, coshf,
sinhf, и tanhf были добавлены из проекта CORE-MATH.

Следование стандартам не предоставляет много в плане гарантий, так как они допускают некоторые вариации.

Кроме того, периодически возникают ошибки и регрессии; например, changelog для версии 2.35 упоминает

[28185] математика: неточная функция j0f (снова)

Что касается определения того, изменилось ли поведение, лучший подход – тестировать с использованием вашего собственного кода. Если вы можете настроить тестовый стенд для отслеживания разработки glibc, вы даже можете сообщать о вариациях и, возможно, исправлять их, если они отражают реальную проблему.

есть ли гарантия, что математические функции в более новых версиях glibc будут давать точно такие же результаты?

Нет.

Две причины:

  1. это не гарантия, которую легко обеспечить: вы фактически не могли бы больше улучшать функции,
  2. обычно это гарантия, которую сложно дать: ваша среда может измениться достаточно, чтобы тот же C код давал разные результаты с плавающей запятой на том же оборудовании. Оба могут быть правильными.

И, возможно, связанный вопрос, есть ли простой способ проверить, что изменилось между двумя версиями библиотеки?

в терминах результатов:

Напишите код, который берет соответствующую выборку пространства возможных входных данных, статически свяжите его со старой библиотекой; сделайте то же самое с новой библиотекой. Пусть оба выполняются параллельно / поочередно на одних и тех же образцах пространства входных данных и сравнивайте результаты в лоб.

В терминах кода:

git log glibc-2.17..glibc sysdeps/ieee754/{dbl-64,flt-32,*.c,*.h} чтобы увидеть логи,
git diff glibc-2.17..glibc sysdeps/ieee754/{dbl-64,flt-32,*.c,*.h} чтобы увидеть измененные файлы, лежащие в основе реализации glibc 32 и 64 для машин, совместимых с IEEE754 (то есть, вероятно, вашей).

По моему скромному мнению (и опыту), это относительно устоявшийся подход – не полагаться на реализацию математических функций в libc в рамках симуляционных фреймворков, так как они могут различаться для разных пользователей, и тогда ваши юнит-тесты могут не проходить у одного пользователя и проходить у другого. Признаюсь, написать cos, который бы был лучше, чем в текущем glibc, не легко! Но это функция, оптимизированная, чтобы быть хорошей для “среднего ожидаемого случая использования”; для некоторых приложений может быть важнее, чтобы разностные частные функции sin приближались к cos, для некоторой точности вокруг кратных pi, для других числовая стабильность для больших входных данных, для некоторых производительность…

.

Ответ или решение

Гарантии на консистентность математических функций в разных версиях glibc вызывают интерес и профессиональную заинтригованность, особенно в контексте задач, где критична точность и воспроизводимость вычислений. Эта ситуация особенно актуальна в высокоточных симуляциях, таких как Монте-Карло для моделирования частиц, где каждая переменная или модификация библиотеки может существенно влиять на конечный результат. Давайте разберем насколько гарантированна консистентность таких функций и что предпринимать в случае, если различия возникают.

Теория: Что представляет собой glibc?

glibc – это стандартная библиотека языка программирования C, которая обеспечивает системные вызовы и функции C стандартной библиотеки, включая математические функции. Эти функции разрабатываются с целью следования стандартам (например, IEEE 754 для чисел с плавающей точкой), однако абсолютная консистентность между разными версиями не всегда гарантируется. Проблема в том, что со временем в glibc могут внедряться оптимизации, исправления ошибок и изменения на основании новых технических стандартов, что может напрямую отражаться на выходных данных функций.

Пример: Изменения между версиями glibc

Было отмечено, что в версии 2.41 оптимизированы и корректно округлены функции exp10m1f, exp2m1f, log10f, lgammaf и другие. Это обновления приведены в контексте проекта CORE-MATH, целью которого является улучшение точности и эффективности. Исторические записи также показывают случаи исправлений недочетов, например, в версии 2.35, что говорит о постоянной эволюции библиотеки.

Стоит воспринимать эти изменения как часть нормального процесса улучшений и контроля качества. Однако, с учетом особенностей реализации математических операций, различия в результатах могут быть наблюдаемы даже на одном и том же оборудовании и с одним и тем же набором данных.

Применение: Как тестировать и адаптироваться к изменениям?

  1. Тестирование кода: Один из лучших способов определить отличия в поведении – это тестирование. Вы можете создать набор тестов, который работает со статической линковкой библиотеки предыдущей версии и сравнить результаты с новой версией. Это позволит непосредственно увидеть, где изменения приводят к иным результатам.

  2. Анализ изменений: Используя системы контроля версий, такие как git, вы можете произвести анализ изменений в коде библиотеки с помощью команд git log и git diff между интересующими вас версиями. Это позволит получить более детальную картину того, какие аспекты математических операций изменились.

  3. Разработка собственных функций: В ситуациях, где стабильность особенно важна, некоторые команды разрабатывают свои версии критичных математических функций. Это позволяет контролировать исполнение функций и обеспечивать консистентность независимо от изменений в стандартных библиотеках.

  4. Тесное взаимодействие с сообществом разработчиков: Сообщество glibc активно и открыто к обратной связи. Если различия существенны и влияют на важные вычисления, возможно инициировать диалог о потенциальной регрессии или предложениях по улучшению.

В заключение, разумное восприятие изменений в glibc и проактивные меры по тестированию и адаптации к ним помогут сохранить надежность и конкурентоспособность ваших приложений. Тщательное тестирование и понимание возможных источников различий между версиями – необходимая практика для всех, кто работает в областях, требующих высокой точности вычислений.

Оцените материал
Добавить комментарий

Капча загружается...