| namespace Eigen { | |
| /** \page TopicVectorization Vectorization | |
| %Eigen performs \em explicit vectorization: instead of relying on the compiler to auto-vectorize scalar | |
| loops, %Eigen's evaluators generate SIMD instructions directly through a portable wrapper layer (the | |
| "packet math" layer). This page gives an overview of which instruction sets are supported, how | |
| vectorization is enabled, what gets vectorized, and how to control it. | |
| \eigenAutoToc | |
| \section TopicVectorizationArch Supported instruction sets | |
| On CPUs, %Eigen provides vectorized kernels for the following instruction sets: | |
| <table class="manual"> | |
| <tr><th>Architecture</th><th>Instruction sets</th><th>Notes</th></tr> | |
| <tr><td>x86 / x86-64</td><td>SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2+FMA, AVX512 (incl. the DQ, VL, FP16, and BF16 extensions)</td> | |
| <td>Selected from the compiler's target flags (e.g. \c -mavx2 \c -mfma, \c /arch:AVX2)</td></tr> | |
| <tr class="alt"><td>ARM / AArch64</td><td>NEON, SME; SVE as an opt-in backend</td> | |
| <td>SVE requires \c EIGEN_ARM64_USE_SVE and a fixed vector length | |
| (<tt>-msve-vector-bits=N</tt>). The SME backend accelerates real and complex matrix | |
| products and is selected automatically when the compiler targets SME2 | |
| (<tt>-march=armv9.2-a+sme2</tt>, or a <tt>-mcpu</tt> that implies it) \em without | |
| <tt>-msve-vector-bits</tt>, since a fixed length would pin the kernels to one runtime | |
| streaming vector length; everything else keeps the NEON path. Define | |
| \c EIGEN_ARM64_NO_SME to opt out, or \c EIGEN_ARM64_USE_SME to turn a toolchain that | |
| cannot provide it into a build error instead of a NEON fallback. Double precision | |
| additionally needs the optional FEAT_SME_F64F64 extension (<tt>+sme-f64f64</tt>, or a | |
| <tt>-mcpu</tt> that implies it); without it \c double and \c std::complex<double> keep | |
| the generic kernel</td></tr> | |
| <tr><td>PowerPC</td><td>AltiVec, VSX, MMA</td><td></td></tr> | |
| <tr class="alt"><td>IBM Z (s390x)</td><td>ZVector</td><td></td></tr> | |
| <tr><td>MIPS</td><td>MSA</td><td></td></tr> | |
| <tr class="alt"><td>LoongArch</td><td>LSX</td><td></td></tr> | |
| <tr><td>RISC-V</td><td>RVV 1.0</td> | |
| <td>Requires \c EIGEN_RISCV64_USE_RVV10 and a fixed vector length | |
| (<tt>-mrvv-vector-bits=zvl</tt>)</td></tr> | |
| <tr class="alt"><td>Qualcomm Hexagon</td><td>HVX</td><td></td></tr> | |
| </table> | |
| In addition, the same packet abstraction is used to generate efficient device code for CUDA and HIP | |
| GPUs (see \ref TopicCUDA) and for SYCL devices. | |
| Vectorization is enabled automatically whenever the compiler's target flags advertise one of the | |
| supported instruction sets. You can check what a given translation unit ended up using by printing | |
| Eigen::SimdInstructionSetsInUse(): | |
| \code | |
| std::cout << Eigen::SimdInstructionSetsInUse() << std::endl; | |
| \endcode | |
| \section TopicVectorizationWhat What gets vectorized | |
| Vectorization applies across %Eigen, most notably to: | |
| - assignment of coefficient-wise expressions (e.g. <tt>a = 2*b + c.cwiseProduct(d)</tt>), | |
| including the fused evaluation loops described in \ref TopicEigenExpressionTemplates; | |
| - reductions such as sum(), minCoeff(), squaredNorm(), and their partial (colwise/rowwise) | |
| variants; | |
| - matrix-matrix and matrix-vector products, and through them most decompositions; | |
| - many coefficient-wise math functions; the \ref CoeffwiseMathFunctions "math function catalogue" | |
| lists, for every function, the instruction sets and scalar types with a vectorized | |
| implementation. | |
| The scalar types with SIMD support depend on the instruction set; \c float, \c double, 32- and | |
| 64-bit integers, and <tt>std::complex<float></tt>/<tt>std::complex<double></tt> are covered on the | |
| main backends, with \c Eigen::half and \c Eigen::bfloat16 vectorized where hardware support exists | |
| (e.g. AVX512FP16, NEON). Types without SIMD support, such as <tt>long double</tt> or custom scalar | |
| types, simply use scalar code. | |
| Fixed-size objects are vectorized when their size is a multiple of the packet size; this is where | |
| the alignment rules for \ref TopicFixedSizeVectorizable "fixed-size vectorizable types" come from. | |
| Dynamic-size objects use aligned heap allocation plus vectorized loops with scalar prologues and | |
| epilogues where needed. Unaligned loads and stores are used where alignment cannot be established; | |
| this is enabled by default and controlled by \c EIGEN_UNALIGNED_VECTORIZE. | |
| \section TopicVectorizationCtrl Controlling vectorization | |
| Vectorization is meant to be transparent, but the following macros, all documented in | |
| \ref TopicPreprocessorDirectives, let you control it: | |
| - \c EIGEN_DONT_VECTORIZE disables explicit vectorization entirely. | |
| - \c EIGEN_MAX_ALIGN_BYTES and \c EIGEN_MAX_STATIC_ALIGN_BYTES bound the alignment %Eigen | |
| requests for heap and static data respectively; note that changing them affects the ABI. | |
| - \c EIGEN_UNALIGNED_VECTORIZE=0 restricts vectorization to expressions whose destination is | |
| aligned. | |
| - \c EIGEN_FAST_MATH selects between faster and more accurate vectorized math functions. | |
| Disabling vectorization also drops %Eigen's alignment: outside GPU compilation, | |
| \c EIGEN_DONT_VECTORIZE sets the ideal alignment to zero, and the defaults for | |
| \c EIGEN_MAX_STATIC_ALIGN_BYTES and \c EIGEN_MAX_ALIGN_BYTES follow it. Fixed-size vectorizable | |
| types then lose their over-alignment — \c alignof(Vector4f) drops from 16 to 4 on a typical x86-64 | |
| build — so translation units that disagree about \c EIGEN_DONT_VECTORIZE disagree about the ABI. | |
| Define the alignment macros explicitly and identically everywhere if you need the boundary to stay | |
| fixed. See \ref TopicUnalignedArrayAssert for what the alignment is there for. | |
| When compiling CUDA sources, host-side SIMD must be disabled; see \ref TopicCUDA. | |
| \section TopicVectorizationInternals Under the hood | |
| Vectorized kernels are written in terms of generic packet primitives — pload(), pstore(), padd(), | |
| pmul(), predux(), and friends — declared in Eigen/src/Core/GenericPacketMath.h and specialized for | |
| each instruction set under <tt>Eigen/src/Core/arch/</tt>. Expression evaluators query each | |
| expression's packet support and alignment at compile time to choose between scalar, linear, | |
| sliced, and unrolled vectorized evaluation loops. Custom functors can participate in vectorization | |
| by providing a \c packetOp() and advertising \c PacketAccess in their traits; see | |
| \ref TopicCustomizing_Functors. | |
| */ | |
| } |