Я вступил в сборку трансцендентных математических функций библиотеки C с MSVC в режиме fp: strict. Кажется, что все они следуют одной и той же схеме, вот что происходит для sin
,
Сначала идет процедура отправки из файла с именем «disp_pentium4.inc». Проверяет, является ли переменная ___use_sse2_mathfcns
был установлен; если так, звонит __sin_pentium4
иначе звонит __sin_default
,
__sin_pentium4
(в «sin_pentium4.asm») начинается с передачи аргумента из fpu x87 в регистр xmm0, выполняет вычисления с использованием инструкций SSE2 и загружает результат обратно в fpu.
__sin_default
(в «sin.asm») хранит переменную в стеке x87 и просто вызывает fsin
,
Таким образом, в обоих случаях операнд помещается в стек x87 и возвращается к нему, что делает его прозрачным для вызывающей стороны, но если ___use_sse2_mathfcns
определяется, операция фактически выполняется в SSE2, а не в x87.
Это поведение очень интересно для меня, потому что трансцендентные функции x87 печально известны тем, что они немного отличаются в зависимости от реализации, в то время как данный фрагмент кода SSE2 всегда должен давать воспроизводимые результаты.
Есть ли способ точно определить, во время компиляции или во время выполнения, что будет использоваться путь кода SSE2? Я не опытный специалист по написанию ассемблера, поэтому, если для этого потребуется написать какую-либо сборку, будет полезен пример кода.
Я нашел ответ путем тщательного изучения математики. Это контролируется методом под названием _set_SSE2_enable
, Это публичный символ документально Вот:
Включает или отключает использование Streaming SIMD Extensions 2 (SSE2)
инструкции по математике CRT. (Эта функция недоступна на
x64 архитектуры, потому что SSE2 включен по умолчанию.)
Это приводит к тому, что вышеупомянутому флагу ___use_sse2_mathfcns будет присвоено предоставленное значение, что эффективно разрешает или запрещает использование подпрограмм _pentium4 SSE2.
В документации упоминается, что это влияет только на некоторые трансцендентные функции, но, глядя на разборку, похоже, это затрагивает каждого из них.
Редактировать: вход в каждую функцию показывает, что все они доступны в SSE2, за исключением следующего:
Sqrt является крупнейшим нарушителем, но его легко реализовать в SSE2 с использованием встроенных функций. Для других нет простого решения, кроме, возможно, использования сторонней библиотеки, но я, вероятно, могу обойтись без.
Почему бы не использовать собственную библиотеку вместо среды выполнения C? Это обеспечило бы еще более надежную гарантию согласованности между компьютерами (предположительно, среда выполнения C предоставляется в виде DLL и может немного изменяться со временем).
Я бы посоветовал CRlibm. Если вы уже нацеливаетесь на SSE2, и если вы не намеревались менять режим округления FPU, вы находитесь в идеальных условиях для его использования и не найдете более точной реализации.
Короткий ответ заключается в том, что вы НЕ можете точно сказать В СВОЕМ КОДЕ, что будет делать библиотека, если только вы не задействуете специфические детали реализации библиотеки. Это сделало бы код полностью непереносимым — даже две разные сборки одного и того же компилятора могут изменить внутреннюю часть библиотеки.
Конечно, если переносимость не является проблемой, тогда использование extern <type> ___use_sse2_mathfcns;
и проверка, правда ли это, будет работать.
Я ожидаю, что если процессор имеет SSE2 и вы используете достаточно современную библиотеку, он будет использовать SSE2 везде, где это возможно. Но сказать это наверняка — это другое дело.
Если это критично для вашего кода, то реализуйте свои собственные трансцендентные функции и используйте их — это единственный способ гарантировать тот же результат. Или используйте некоторый подходящий встроенный ассемблерный (или трансцендентный) код для вычисления выбранного sin
, cos
и т. д., и сравните их с sin()
а также cos()
функции, предоставляемые библиотекой.