Using Intel MKL on AMD CPUs¶
Intel oneMKL provides optimized BLAS, LAPACK, FFT, and other numerical routines. On AMD processors, however, some MKL versions have historically selected less-optimized code paths based on CPU identification, which can significantly reduce performance.
This behaviour has been documented by Intel, AMD, and HPC centres. AMD's EPYC tuning guide, for example, describes cases where MKL did not select the AVX2 implementation by default on EPYC processors.
Workaround¶
The CPU partition of Perun uses AMD processors based on the Zen 5c microarchitecture, so it is recommended to use the following workaround to ensure that MKL uses the optimized code paths. There is a special module called fakeintel that can be loaded to make MKL believe it is running on an Intel CPU, which will force it to use the optimized code paths. One has to keep it mind that this load only workaround not the library itself, so it is necessary to load the fakeintel module along with the desired Intel module.
Usage¶
To use the fakeintel module is simple:
module load vasp-intel
mpirun -np 4 vasp_std # VASP goes slow
module load fakeintel
mpirun -np 4 vasp_std # VASP goes fast
module unload fakeintel
mpirun -np 4 vasp_std # VASP goes slow again
Performance comparison¶
The perfromance difference can be significant, but in the end it depends on the aplicatoin and the version of Intel libraries used. The following table shows the performance of BLAS/LAPACK routines on AMD EPYC 9845 (Zen 5c) with and without the fakeintel module loaded. The tests were performed using Intel MKL version 2025.2.0.
| Routine | Size | Without fakeintel [GFLOP/s] |
With fakeintel [GFLOP/s] |
Speedup [%] |
|---|---|---|---|---|
| daxpy | 256 | 8.523 | 12.785 | 50.01 |
| dgemv | 256 | 23.115 | 27.536 | 19.13 |
| dgemm | 256 | 184.617 | 878.039 | 375.60 |
| dgetrf | 256 | 6.221 | 6.453 | 3.73 |
| dpotrf | 256 | 6.767 | 0.986 | -85.43 |
| dgesv | 256 | 6.179 | 6.457 | 4.50 |
| daxpy | 512 | 9.318 | 20.361 | 118.51 |
| dgemv | 512 | 18.393 | 19.197 | 4.37 |
| dgemm | 512 | 610.988 | 2101.980 | 244.03 |
| dgetrf | 512 | 32.146 | 31.704 | -1.37 |
| dpotrf | 512 | 51.615 | 80.713 | 56.38 |
| dgesv | 512 | 30.362 | 31.261 | 2.96 |
| daxpy | 1024 | 10.780 | 25.570 | 137.20 |
| dgemv | 1024 | 18.387 | 24.084 | 30.98 |
| dgemm | 1024 | 2228.815 | 4945.587 | 121.89 |
| dgetrf | 1024 | 111.525 | 125.336 | 12.38 |
| dpotrf | 1024 | 108.575 | 226.515 | 108.63 |
| dgesv | 1024 | 105.547 | 121.019 | 14.66 |
As one can see, the performance difference can be significant, especially for larger matrix sizes and certain routines. For example, the dgemm routine shows a speedup of over 375% for size 256 and over 244% for size 512 when using the fakeintel module. However, some routines like show slowdown (e.g., dpotrf for size 256) when using the fakeintel module, indicating that the performance impact can vary depending on the specific routine and matrix size. These measurements were taken with warm up runs from large ensamble of random matrices, so the results are representative of typical usage scenarios and are reproducible. The performance difference can be significant, but in the end it depends on the application and the version of Intel libraries used.