Я пишу числовое программное обеспечение в реальном времени на C ++, которое в настоящее время компилируется с Visual-C ++ 2008.
Теперь используется «быстрая» модель с плавающей запятой ( / fp: fast
), различные оптимизации, большинство из которых полезны для меня. случай, но конкретно:
a/b -> a*(1/b) Division by multiplicative inverse
слишком численно нестабильно для большого количества m у расчетов.
(см .: Оптимизация с плавающей запятой Microsoft Visual C ++ )
Переход на / fp: точный
заставляет мое приложение работать более чем в два раза медленнее. Возможна либо точная настройка оптимизатора (т.е.отключить эту оптимизацию) или как-то обойти ее вручную?
- Фактический пример минимального кода: -
void test(float a, float b, float c,
float &ret0, float &ret1) {
ret0 = b/a;
ret1 = c/a;
}
{ {1}} [мой фактический код в основном состоит из алгоритмов, связанных с матрицами]
Вывод: VC (cl, версия 15, 0x86):
divss xmm0,xmm1
mulss xmm2,xmm0
mulss xmm1,xmm0
Наличие одного div вместо двух является большой численной проблемой, ( xmm0, предварительно загружен 1.0f из ОЗУ), так как в зависимости от значений xmm1,2 (которые могут быть в разных диапазонах) вы можете потерять большую точность (компиляция без SSE выводит аналогичный код stack-x87-FPU).
Заключение функции в оболочку
#pragma float_control( precise, on, push )
...
#pragma float_control(pop)
Решает проблему точности, но, во-первых, она доступна только на уровне функции (глобальная область видимости), а во-вторых, предотвращает встраивание функции (т. Е. Штрафы за скорость слишком велики. high)
"точный" вывод также преобразуется в "двойное" вперед и назад:
divsd xmm1,xmm2
cvtsd2ss xmm1,xmm1
divsd xmm1,xmm0
cvtpd2ps xmm0,xmm1
(Странное) решение, которое я нашел: всякий раз, когда деление на одно и то же значение в функции - добавьте немного эпсилона:
a/b; c/b
->
a/(b+esp1); c/(b+esp2)
Также избавляет вас от случайного деления на ноль
Добавьте
#pragma float_control( precise, on)
перед вычислениями и
#pragma float_control( precise,off)
после них. Думаю, это должно помочь.
В этом документе говорится, что вы можете управлять оптимизацией с плавающей точкой на построчной основе с помощью прагм.
Можете ли вы поместить функции, содержащие эти вычисления, в отдельный файл исходного кода и скомпилировать только этот файл с другими настройками?
Я не знаю, безопасно ли это, вам нужно проверить!
Также есть __assume. Вы можете использовать __assume (a / b! = (A * (1 / b))). На самом деле я никогда не использовал __assume, но теоретически он существует именно для точной настройки оптимизатора.