I'm collecting what we have found out and I'm concentrating for the moment on the call Exp[data]
where data
is a vector of reals in machine precision. For this, the following two statements seem to apply in Mathematica 11.3 with a modern CPU:
- Single instruction, multiple data (SIMD) instructions are used provided by the MMX technology and Streaming SIMD Extensions
- Mathematica employs the Intel Integrated Performance Primitives library under the hood. For the
Exp
case, the function ippsExp_64fc_A53 is called.
Using a slightly more complex example, like
Exp[x]*Log[x + 1.0]
shows that all parts of the expression are relaid to the IPP libraries
- libippvm.so (ippsLn_64f_A53)
- libippvm.so (ippsExp_64f_A53)
- libipps.so (ippsAddC_64f)
- libippvm.so (ippsMul_64f_A53)
Evidently, not all functions are available in the IPP and then it gets complicated and there is no general rule. For instance
data = RandomReal[1, 10^7];
Do[LogGamma[data], {200}] // AbsoluteTiming
Do[Log[Gamma[data]], {200}] // AbsoluteTiming
(* {10.2816, Null} *)
(* {9.09973, Null} *)
It appears that the Gamma
function is implemented in libWolframEngine
and LogGamma
is a combination of Wolfram code for Gamma
and the log function from libm
on my machine. The second call however uses IPP for the logarithm and appears to be a bit faster here.
As soon as we set a specific precision, different implementations are used that partly employ the The GNU Multiple Precision Arithmetic Library. As expected, the runtime drops significantly several orders of magnitude (100x - 1000x).
My second question was, how this relates to compiled code. I want to differentiate two cases
- A parallelized compiled function that gets one number and is made listable.
- A compiled function that receives a tensor of numbers and calls
Exp[list]
in its body
In the first case, it appears that no vectorization is used at all. The parallelism comes from spawning multiple threads. The second case is more interesting. My tests showed that even in the compiled code, IPP library functions are called. For simple cases, however, the runtime of the compiled code cannot compete with simply using high-level calls.
Exp
and friends [software.intel.com/en-us/… - so my guess would be Mathematica uses MKL functions for this. I'd also guess that the MKL vector math functions beat the average C compiler - otherwise, why would C programmers use the (proprietary) MKL, for what is basically a one-liner in C. $\endgroup$LogGamma
that don't perform so well which would support the MKL idea if these functions are not covered there. $\endgroup$Exp[data2]
takes about as long asExp[Developer`ToPackedArray@data2]
.data3
cannot be repacked. What I would suggest is thatExp[data2]
somehow takes advantage of the MKL without actually performing theToPackedArray
operation, since the result comes back unpacked. $\endgroup$Exp[data]
is calling the Intel IPP functionippsExp_64f_A53
with (on my machine) vectors of length 125000 (a total of 80 calls spread over 4 threads). $\endgroup$