Doc: Warn that product results depend on the detected cache sizes libeigen/eigen!3440 Closes #3211 Co-authored-by: Rasmus Munk Larsen <rmlarsen@gmail.com>
diff --git a/Eigen/src/Core/products/GeneralBlockPanelKernel.h b/Eigen/src/Core/products/GeneralBlockPanelKernel.h index 6cf76f5..b29edc3 100644 --- a/Eigen/src/Core/products/GeneralBlockPanelKernel.h +++ b/Eigen/src/Core/products/GeneralBlockPanelKernel.h
@@ -2733,7 +2733,7 @@ } // end namespace internal /** \returns the currently set level 1 cpu cache size (in bytes) used to estimate the ideal blocking size parameters. - * \sa setCpuCacheSize */ + * \sa setCpuCacheSizes */ inline std::ptrdiff_t l1CacheSize() { std::ptrdiff_t l1, l2, l3; internal::manage_caching_sizes(GetAction, &l1, &l2, &l3); @@ -2741,7 +2741,7 @@ } /** \returns the currently set level 2 cpu cache size (in bytes) used to estimate the ideal blocking size parameters. - * \sa setCpuCacheSize */ + * \sa setCpuCacheSizes */ inline std::ptrdiff_t l2CacheSize() { std::ptrdiff_t l1, l2, l3; internal::manage_caching_sizes(GetAction, &l1, &l2, &l3); @@ -2749,7 +2749,7 @@ } /** \returns the currently set level 3 cpu cache size (in bytes) used to estimate the ideal blocking size parameters. - * \sa setCpuCacheSize */ + * \sa setCpuCacheSizes */ inline std::ptrdiff_t l3CacheSize() { std::ptrdiff_t l1, l2, l3; internal::manage_caching_sizes(GetAction, &l1, &l2, &l3); @@ -2758,6 +2758,8 @@ /** Set the cpu L1, L2 and L3 cache sizes (in bytes). * These values are used to adjust blocking sizes and, on some backends, the size crossovers between kernels. + * By default they are queried from the CPU that runs the first product. Setting them explicitly makes the + * blocking, and therefore the rounding of the results, independent of that CPU; see \ref TopicPitfalls_reproducibility. * * \sa computeProductBlockingSizes */ inline void setCpuCacheSizes(std::ptrdiff_t l1, std::ptrdiff_t l2, std::ptrdiff_t l3) {
diff --git a/doc/Pitfalls.dox b/doc/Pitfalls.dox index b3f49bc..c8434e3 100644 --- a/doc/Pitfalls.dox +++ b/doc/Pitfalls.dox
@@ -146,5 +146,24 @@ For coefficient-wise Boolean negation use <tt>!A.array()</tt>; for coefficient-wise XOR use <tt>A.array() != B.array()</tt>. +\section TopicPitfalls_reproducibility Bitwise reproducibility of products + +%Eigen does not promise bitwise identical floating-point results across machines, builds, or runs. Matrix +products, triangular solves, and the decompositions built on them split the summation into blocks sized to the +CPU caches, and floating-point addition is not associative, so a different blocking can change the last bits of +the result. The cache sizes are queried once, on the thread that runs the first such operation, and are kept for +the life of the process. On CPUs that mix core types with different caches, such as Intel processors with +P-cores and E-cores, the results of a program can therefore depend on which kind of core happened to run its +first product. The number of threads and the instruction set enabled at compile time change the results in the +same way. + +Where bitwise reproducibility matters, either: + - pin the process to one core type, for example with \c taskset on Linux or <tt>start /affinity</tt> on Windows, + or + - call \c Eigen::setCpuCacheSizes() with fixed values before the first product, so that the blocking no longer + depends on the core that is running. + +In both cases, also fix the number of threads and the build configuration. + */ }