Fusão multiplicar adicionar e modos de arredondamento padrão

Com o GCC 5.3, o seguinte código compilar com -O3 -fma

 float mul_add(float a, float b, float c) { return a*b + c; } 

produz o seguinte conjunto

 vfmadd132ss %xmm1, %xmm2, %xmm0 ret 

Eu observei o GCC fazendo isso com o -O3 já no GCC 4.8 .

Clang 3.7 com -O3 -mfma produz

 vmulss %xmm1, %xmm0, %xmm0 vaddss %xmm2, %xmm0, %xmm0 retq 

mas o Clang 3.7 com -Ofast -mfma produz o mesmo código que o GCC com -O3 fast .

Estou surpreso que GCC faz com -O3 porque a partir desta resposta diz

O compilador não tem permissão para fundir uma adição separada e multiplicar, a menos que você permita um modelo de ponto flutuante descontraído.

Isso ocorre porque um FMA tem apenas um arredondamento, enquanto um ADD + MUL tem dois. Portanto, o compilador violará o comportamento estrito do ponto flutuante do IEEE ao fundir.

No entanto, a partir deste link , diz

Independentemente do valor de FLT_EVAL_METHOD, qualquer expressão de ponto flutuante pode ser contratada, ou seja, calculada como se todos os resultados intermediários tivessem intervalo e precisão infinitos.

Então agora estou confuso e preocupado.

  1. O GCC é justificado ao usar o FMA com o -O3 ?
  2. A fusão viola o comportamento estrito do ponto flutuante do IEEE?
  3. Se a fusão violar o ponto de vista flutuante do IEEE e o GCC retornar __STDC_IEC_559__ isso não é uma contradição?

Como o FMA pode ser emulado em software , parece haver dois switches do compilador para o FMA: um para dizer ao compilador para usar o FMA em cálculos e outro para informar ao compilador que o hardware tem FMA.


Aproximadamente isto pode ser controlado com a opção -ffp-contract . Com o GCC, o padrão é -ffp-contract=fast e com o Clang não é. Outras opções, como -ffp-contract=on e -ffp-contract=off , não produzem a instrução FMA.

Por exemplo, o Clang 3.7 com -O3 -mfma -ffp-contract=fast produz vfmadd132ss .


Eu verifiquei algumas permutações do #pragma STDC FP_CONTRACT definido como ON e OFF com -ffp-contract definido como on , off e fast . Em todos os casos eu também usei -O3 -mfma .

Com o GCC, a resposta é simples. #pragma STDC FP_CONTRACT ON ou OFF não faz diferença. Apenas -ffp-contract .

GCC usa fma com

  1. -ffp-contract=fast (padrão).

Com Clang usa fma

  1. com -ffp-contract=fast .
  2. com -ffp-contract=on (padrão) e #pragma STDC FP_CONTRACT ON (o padrão é OFF ).

Em outras palavras, com o Clang você pode obter fma com #pragma STDC FP_CONTRACT ON (já que -ffp-contract=on é o padrão) ou com -ffp-contract=fast . -ffast-math (e, portanto, -Ofast ) set -ffp-contract=fast .


Eu olhei para MSVC e ICC.

Com o MSVC, ele usa a instrução fma com /O2 /arch:AVX2 /fp:fast . Com MSVC /fp:precise é o padrão.

Com ICC usa fma com -O3 -march=core-avx2 (acctually -O1 é suficiente). Isso ocorre porque, por padrão, o ICC usa o -fp-model fast . Mas o ICC usa fma mesmo com o -fp-model precise . Para desativar o fma com o uso do ICC -fp-model strict ou -no-fma .

Portanto, por padrão, o GCC e o ICC usam fma quando o fma está habilitado (com -mfma para GCC / Clang ou -march=core-avx2 com ICC), mas o Clang e o MSVC não.

Não viola o IEEE-754, porque o IEEE-754 adia para idiomas neste ponto:

Um padrão de idioma também deve definir e exigir implementações para fornecer atributos que permitem e não permitem otimizações de alteração de valor, separadamente ou coletivamente, para um bloco. Essas otimizações podem include, mas não estão limitadas a:

– Síntese de uma operação fusedMultiplyAdd de uma multiplicação e uma adição.

No padrão C, o pragma STDC FP_CONTRACT fornece os meios para controlar essa otimização de mudança de valor. Portanto, o GCC é licenciado para executar a fusão por padrão, desde que permita desabilitar a otimização configurando STDC FP_CONTRACT OFF . Não apoiar isso significa não aderir ao padrão C.

Quando você citou que o multiplexado fundido é permitido, você deixou de fora a condição importante “a menos que o pragma FP_CONTRACT esteja desativado”. Que é um recurso novo em C (eu acho que foi introduzido em C99) e foi absolutamente necessário pelo PowerPC, que todos fundiram multiplica-add desde o início – na verdade, x * y era equivalente a fma (x, y, 0) e x + y foi equivalente a fma (1,0, x, y).

FP_CONTRACT é o que controla o fusionado multiplicar / adicionar, não FLT_EVAL_METHOD. Embora se FLT_EVAL_METHOD permita maior precisão, a contratação é sempre legal; basta fingir que as operações foram realizadas com precisão muito alta e depois arredondadas.

A function fma é útil se você não quer a velocidade, mas a precisão. Ele calculará o resultado contratado lentamente, mas corretamente, mesmo que não esteja disponível no hardware. E deve ser embutido se estiver disponível no hardware.