The /fp:contract flag and changes to FP modes in VS2022
In this blog we will cover a new feature we have added to the MSVC version 17.0 compiler in VS2022 that impacts the generation of Floating-Point contractions such as Fused Multiply Add (FMA) instructions. We will cover how FMA contractions are supported in pre-VS2022 MSVC compiler releases, a new /fp:contract flag and changes to existing Floating-Point pragmas in VS2022 MSVC compiler allowing explicit control over generation of contractions
A contraction, as used here, is where two operations in the source code are performed by a single instruction in the executable code. Examples are Fused Multiply-Add (FMA) and reciprocal square root. The former computes ((a * b) + c), while the later computes (1/sqrt(a)). The advantages of contractions are increased speed of calculations involved and decrease in the code size of your application. The reason you might not want to use a contraction is because the intermediate result isn’t rounded, so the result might differ slightly from what you would get from separate instructions. That is often not a problem, but we’re particular about what we mean by “precise” and don’t want to promise something we’re not delivering. Giving you control over whether contractions are used or not gives the best of both worlds, where you can get consistent results when you need them and can use contractions when you don’t.
Pre-VS2022 behavior for contractions
In the versions of Visual Studio prior to VS2022, under the default FP mode of /fp:precise, the generation of contractions was inconsistent. This inconsistency was between different platforms and between scalar and vector versions of FMAs. The compiler could generate both scalar and vector versions of FMAs on the ARM and ARM64 platforms. On x86 and x64 platforms supporting FMA instructions, the compiler could only generate vector FMA instructions. We are addressing this inconsistency and updating the documentation on FP modes in VS2022.
VS2022 behavior for contractions
Although contractions tend to improve the performance of your application, they can produce inconsistent results between debug and release builds and ISA targeting (example: SSE2 vs AVX2) and may result in breaking existing assumptions in test coverage. To account for this and address the aforementioned inconsistency, contractions will not be generated by default under the /fp:precise mode on all platforms starting from VS2022 version 17.0. We have introduced a new /fp:contract flag which can be used along with /fp:precise to enable contractions. The /fp:contract flag will enable both vector and scalar contractions on all platforms. The /fp:contract flag and the updated behavior for /fp:precise is documented here. The table below summarizes the behavior for VS2022.
Compiler FP-mode flag | VS2022 17.0 default behavior | VS2022 17.0 behavior with /fp:contract |
/fp:strict | No contractions | Incompatible (compiler error) |
/fp:precise | No contractions | Allow contractions |
/fp:fast | Allow contractions | Allow contractions |
The behavior of Floating-Point pragmas was also modified to agree with the behavior of the Floating-Point flags. The float_control pragmas will now disable contractions when turned on and will restore the previous setting for contractions when turned off. This new behavior is documented for float_control, fenv_access and fp_contract pragmas.
With this new behavior, there can be performance regressions since contractions are no longer generated by default. Adding the /fp:contract flag should mitigate this. The behavior of contractions can be further controlled at a function level using the Floating-Point pragmas.
Note that intrinsic functions such as: fma, fmaf and fmal can still be used to generate FMA machine instructions if the target architecture supports them.
How to enable /fp:contract in VS2022
To enable /fp:contract for your project:
In Visual Studio, add the /fp:contract option in the Additional Options box (Project|Properties|Configuration Properties|C/C++|Command Line|Additional Options)
Figure 1. Add the /fp:contract compiler option for each desired configuration.
Since generation of contractions is an optimization, adding the /fp:contract flag may not produce contractions for debug builds.
If you are upgrading your project from VS2019 to VS2022 and see different floating-point results, these are the things you should check:
- If your code is built with /fp:fast, this can be the expected behavior. /fp:fast allows the compiler to optimize things more aggressively with some loss in FP precision. More optimizations are likely firing in this case.
- If your code is built with /fp:precise (or no /fp model is specified), try throwing /fp:contract to confirm if contractions were the cause of the FP changes. If it was, see if that makes sense for your scenario to continue to throw /fp:contract.
- If your code is built with /fp:strict then let us know, there may be a compiler bug.
Closing Notes
We’d love for you to download Visual Studio 2022 and give it a try to see how the changes described above affect your applications. Your feedback is key to deliver the best experience. If you have any questions, please feel free to ask us below. You can also send us your comments through e-mail. If you encounter problems with the experience or have suggestions for improvement, please Report A Problem or reach out via Developer Community. You can also find us on Twitter @VisualC.
0 comments