Параметры компилятора с плавающей точкой C ++ | предотвращение a / b -> a * (1 / b) - PullRequest
8 голосов
/ 04 августа 2010

Я пишу числовое программное обеспечение в реальном времени на C ++, в настоящее время компилирую его с Visual-C ++ 2008. Теперь использую «быструю» модель с плавающей запятой (/fp:fast), различные оптимизации, большинство из которых полезно в моем случае, но конкретно:

a/b -> a*(1/b) Division by multiplicative inverse

слишком численно нестабилен для многих моих расчетов.

(см .: Microsoft Visual C ++ Оптимизация с плавающей точкой )

Переключение на /fp:precise заставляет мое приложение работать более чем в два раза медленнее.Возможно ли либо оптимизировать оптимизатор (т.е. отключить эту конкретную оптимизацию), либо как-то вручную обойти ее?

- Фактический пример минимального кода: -

void test(float a, float b, float c,
    float &ret0, float &ret1) {
  ret0 = b/a;
  ret1 = c/a;
} 

[myфактический код в основном связан с матричными алгоритмами]

Вывод: 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)

Решает проблему точности, но, во-первых, она доступна только на уровне функций (global-scope), а во-вторых, она предотвращает встраиваниефункция (т. е. слишком высокие штрафы за скорость)

«точный» вывод приводится также к «удвоению» вперед и назад:

 divsd       xmm1,xmm2 
 cvtsd2ss    xmm1,xmm1 
 divsd       xmm1,xmm0 
 cvtpd2ps    xmm0,xmm1 

Ответы [ 5 ]

3 голосов
/ 04 августа 2010

Добавьте

#pragma float_control( precise, on)

до вычисления и

#pragma float_control( precise,off)

после этого.Я думаю, что это должно сделать.

2 голосов
/ 04 августа 2010

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

1 голос
/ 08 августа 2010

Существует также __assume.Вы можете использовать __assume (a / b! = (A * (1 / b))).Я никогда не использовал __assume, но теоретически он существует именно для тонкой настройки оптимизатора.

0 голосов
/ 08 августа 2010

(Странное) решение, которое я нашел: при делении на одно и то же значение в функции - добавьте несколько эпсилон:

    a/b; c/b 

->

    a/(b+esp1); c/(b+esp2)

Также спасает вас отслучайный делитель на ноль

0 голосов
/ 04 августа 2010

Можете ли вы поместить функции, содержащие эти вычисления, в отдельный файл исходного кода и скомпилировать только этот файл с другими настройками?

Я не знаю, насколько это безопасно, вам нужно проверить!

...