Вопрос или проблема
Я работаю с программой моделирования на основе метода Монте-Карло для физики частиц и пытаюсь понять некоторую случайность, которую наблюдаю. Принцип этой программы заключается в том, что она вводит частицу в некоторую среду, а затем использует генератор случайных чисел, чтобы решить, какие типы взаимодействий она имеет. Это позволяет создать множество возможных путей для частицы. Как пользователь, я могу указать начальные значения для 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 будут давать точно такие же результаты?
Нет.
Две причины:
- это не гарантия, которую легко обеспечить: вы фактически не могли бы больше улучшать функции,
- обычно это гарантия, которую сложно дать: ваша среда может измениться достаточно, чтобы тот же 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, что говорит о постоянной эволюции библиотеки.
Стоит воспринимать эти изменения как часть нормального процесса улучшений и контроля качества. Однако, с учетом особенностей реализации математических операций, различия в результатах могут быть наблюдаемы даже на одном и том же оборудовании и с одним и тем же набором данных.
Применение: Как тестировать и адаптироваться к изменениям?
-
Тестирование кода: Один из лучших способов определить отличия в поведении – это тестирование. Вы можете создать набор тестов, который работает со статической линковкой библиотеки предыдущей версии и сравнить результаты с новой версией. Это позволит непосредственно увидеть, где изменения приводят к иным результатам.
-
Анализ изменений: Используя системы контроля версий, такие как
git
, вы можете произвести анализ изменений в коде библиотеки с помощью командgit log
иgit diff
между интересующими вас версиями. Это позволит получить более детальную картину того, какие аспекты математических операций изменились. -
Разработка собственных функций: В ситуациях, где стабильность особенно важна, некоторые команды разрабатывают свои версии критичных математических функций. Это позволяет контролировать исполнение функций и обеспечивать консистентность независимо от изменений в стандартных библиотеках.
-
Тесное взаимодействие с сообществом разработчиков: Сообщество glibc активно и открыто к обратной связи. Если различия существенны и влияют на важные вычисления, возможно инициировать диалог о потенциальной регрессии или предложениях по улучшению.
В заключение, разумное восприятие изменений в glibc и проактивные меры по тестированию и адаптации к ним помогут сохранить надежность и конкурентоспособность ваших приложений. Тщательное тестирование и понимание возможных источников различий между версиями – необходимая практика для всех, кто работает в областях, требующих высокой точности вычислений.