Opções do compilador C ++ do ponto flutuante | Prevenção de A/B -> A* (1/B)
-
25-09-2019 - |
Pergunta
Estou escrevendo software numérico em tempo real, em C ++, atualmente compilando-o com o Visual-C ++ 2008. Agora, usando o modelo de ponto flutuante 'rápido' (/fp:fast
), várias otimizações, a maioria úteis no meu caso, mas especificamente:
a/b -> a*(1/b) Division by multiplicative inverse
é numericamente instável demais para o A-Lot dos meus cálculos.
(Vejo: Microsoft Visual C ++ Otimização de ponto flutuante)
Mudando para /fp:precise
Faz meu aplicativo funcionar mais do que duas vezes mais lento. É possível ajustar o otimizador (ou seja, desativar essa otimização específica) ou, de alguma forma, ignorá-lo manualmente?
- Exemplo real de código mínimo: -
void test(float a, float b, float c,
float &ret0, float &ret1) {
ret0 = b/a;
ret1 = c/a;
}
Meu código real é principalmente algoritmos relacionados à matriz
Saída: VC (CL, versão 15, 0x86) é:
divss xmm0,xmm1
mulss xmm2,xmm0
mulss xmm1,xmm0
Ter uma div, em vez de dois é um grande problema numericamente, (XMM0, é pré -carregado com 1,0F da RAM), como dependendo dos valores de XMM1,2 (que podem estar em diferentes faixas), você pode perder muita precisão ( Compilando sem SSE, produza o código Stack-X87-FPU semelhante).
Envolvendo a função com
#pragma float_control( precise, on, push )
...
#pragma float_control(pop)
Resolva o problema da precisão, mas, em primeiro lugar, ele só está disponível em um nível de função (escopo global) e, segundo, impede que a função (ou seja, as penalidades de velocidade são muito altas)
A saída 'precisa' está sendo lançada para 'dobrar' para frente e para trás como poço:
divsd xmm1,xmm2
cvtsd2ss xmm1,xmm1
divsd xmm1,xmm0
cvtpd2ps xmm0,xmm1
Solução 5
(Estranha) Solução que encontrei: sempre que dividir o mesmo valor em uma função - adicione um pouco de epsilon:
a/b; c/b
->
a/(b+esp1); c/(b+esp2)
Também salva você da div ocasional por zero
Outras dicas
Adicione o
#pragma float_control( precise, on)
antes do cálculo e
#pragma float_control( precise,off)
depois disso. Eu acho que isso deveria fazer isso.
Esse documento afirma que você pode controlar as otimizações de apontar float, de forma linha a linha, usando Pragmas.
Há também __assume. Você pode usar __assume (a/b! = (A*(1/b))). Na verdade, nunca usei __assume, mas, em teoria, existe exatamente para ajustar o otimizador.
Você pode colocar as funções que contêm esses cálculos em um arquivo de código -fonte separado e compilar apenas esse arquivo com as diferentes configurações?
Porém, não sei se isso é seguro, você precisará verificar!