Compiler flags#
FFCx generates C kernels. The C compiler flags used to build those kernels can have a substantial effect on performance. Set these flags through the JIT compilation interface, or through your build system when compiling generated C files.
Suggested options#
For GCC and Clang, start with -O2 -ffp-contract=fast. Benchmark the
following configurations on representative forms and polynomial degrees:
Configuration |
Flags |
Use |
|---|---|---|
Starting point |
|
Optimize kernels and permit fused multiply-add instructions. |
Local x86-64 CPU |
|
Also enable instructions supported by the compilation host. |
Additional optimization |
|
Compare against O2; O3 is not faster for every kernel. |
Relaxed floating-point arithmetic |
|
Evaluate when the application’s numerical requirements allow it. |
On x86-64, -march=native can also be added to either O3 configuration.
It can produce binaries that do not run on other processors. Omit it or
select an explicit CPU target supported by every machine that will run the
kernel, including machines sharing a JIT cache. Other architectures have
their own CPU-target options.
-ffp-contract=fast permits the compiler to combine multiplication and
addition into a fused multiply-add (FMA) instruction when the target CPU
supports it. FMA rounds once, rather than rounding the product and sum
separately, so results can differ in their last bits. This option does not
require -ffast-math. Use -ffp-contract=off when separate rounding is
required.
-ffast-math permits broader changes, including reassociation of
floating-point expressions and assumptions about NaNs, infinities and signed
zeros. Check accuracy and convergence for the application before adopting
it. Compare O2 and O3 using the same CPU-target and floating-point options
to distinguish optimization-level effects from changes in arithmetic.
These switches are compiler-specific. For Microsoft Visual C++, start with
["/O2", "/fp:precise"]. Visual Studio 2022 also provides /fp:contract
to permit FMA, subject to the selected target’s instruction support. See the
GCC optimization options,
Clang floating-point options
and MSVC floating-point options
for details.
Setting flags with the FFCx JIT#
Pass a list of C compiler arguments as cffi_extra_compile_args to
ffcx.codegeneration.jit.compile_forms. Given a UFL form a:
from pathlib import Path
from ffcx.codegeneration.jit import compile_forms
compiler_flags = ["-O2", "-ffp-contract=fast"]
compiled_forms, module, code = compile_forms(
[a],
cache_dir=Path(".cache/ffcx"),
cffi_extra_compile_args=compiler_flags,
)
compile_expressions accepts the same keyword argument. FFCx includes
the compiler arguments in its JIT signature, so changing them produces a
different cached module. Use cffi_verbose=True to see the compiler
invocation. Select the C compiler through the build environment, for
example by setting CC=clang before starting Python.
C compiler arguments are separate from FFCx code-generation options such
as scalar_type and sum_factorization, which belong in the
options dictionary. Sum factorisation is enabled by default
where supported.
Setting flags through DOLFINx#
When using DOLFINx, pass the compiler arguments through jit_options:
from dolfinx import fem
compiled_a = fem.form(
a,
jit_options={
"cffi_extra_compile_args": ["-O2", "-ffp-contract=fast"],
},
)
Use form_compiler_options for FFCx code-generation options. DOLFINx
also supports a dolfinx_jit_options.json file in the working directory:
{
"cffi_extra_compile_args": ["-O2", "-ffp-contract=fast"]
}
An explicitly supplied jit_options value takes precedence over the
corresponding file setting.
Compiling generated C files#
The ffcx command generates C source and header files. Apply compiler
flags when building that source, for example in a CMake target:
target_compile_options(my_kernels PRIVATE -O2 -ffp-contract=fast)
Use the compiler options appropriate to the selected compiler and target. Changing FFCx code-generation options does not set these C compiler flags.