# Defined function takes more time than pure/anonymous function. Why? [duplicate]

A function defined by f[x_]:=<code> and applied on every element of a list as f /@ testList seems to take longer than the same anonymous function applied as Function[x,<code>] /@ testList. Why is that? Is there some extra copying of variables in the first case? Is it not the correct way to define functions?

# MWE: Signed volumes

As a MWE, consider a function computing a signed volume defined as follows:

signedVol[lst_] := Det@Table[lst[[i]] - lst[[1]], {i, 2, Length@lst}]


Generate testing data as 100'000 quadruples of points in 3D:

testList = Table[RandomReal[{-5, 5}, {4, 3}], {i, 1, 100000}];


And compare the following two evaluations. In the second, we just replace signedVol by an anonymous function with identical definition:

time = Now; signedVol /@ testList; Print[Now - time]
time = Now; Function[lst, Det@Table[lst[[i]] - lst[[1]], {i, 2, Length@lst}]] /@ testList; Print[Now - time]


On my computer, I am consistently getting ~0.8s for the former, and ~0.3s for the latter. I also tried for 1'000'000 quadruples, and got a consistent ratio, 9.1s vs 2.9s.

# Question

Why is there such a big difference? Is defining a function like this somehow wrong?

I also tried defining signedVol=Function[lst, <code>] and then applying it, which gave me the same results as applying the anonymous function directly (the latter case). So this seems to be a viable option for fast computations with a predefined function. However, I have never seen this being used, and it feels like a strange construction. Is this something people use?

# Version and system

I am using Mathematica 12.3.1 for macOS, and I am on macOS Monterey 12.1

I also defined the same computation by my own explicit formula for the 3D case as follows

signedVol3D[lst_] := (
(lst[[2, 1]] - lst[[1, 1]])*(lst[[3, 2]] -
lst[[1, 2]])*(lst[[4, 3]] - lst[[1, 3]])
+ (lst[[4, 1]] - lst[[1, 1]])*(lst[[2, 2]] -
lst[[1, 2]])*(lst[[3, 3]] - lst[[1, 3]])
+ (lst[[3, 1]] - lst[[1, 1]])*(lst[[4, 2]] -
lst[[1, 2]])*(lst[[2, 3]] - lst[[1, 3]])
- (lst[[4, 1]] - lst[[1, 1]])*(lst[[3, 2]] -
lst[[1, 2]])*(lst[[2, 3]] - lst[[1, 3]])
- (lst[[2, 1]] - lst[[1, 1]])*(lst[[4, 2]] -
lst[[1, 2]])*(lst[[3, 3]] - lst[[1, 3]])
- (lst[[3, 1]] - lst[[1, 1]])*(lst[[2, 2]] -
lst[[1, 2]])*(lst[[4, 3]] - lst[[1, 3]])
)


I did the same experiment as with signedVol and the times are as follows

Code used Time
signedVol /@ testList 0.83 seconds
Function[lst, <code of signedVol>] /@ testList 0.26 seconds
signedVol3D /@ testList 2.30 seconds
Function[lst, <code of signedVol3D>] /@ testList 0.05 seconds

Interestingly, signedVol3D performs worse than signedVol, but the anonymous version of signedVol3D performs better than the anonymous version of signedVol.

# Remark on timing method used

Note that I use the construction time = Now; <code>; Print[Now - time] instead of Timing, because Timing seems to have some unexpected behavior when you run it several times in the same cell -- for example if you run it on the same code multiple times in the same cell, the first evaluation yields faster time than all the following. But there is already a separate question about that.

Mathematica is a pattern matching and rewriting language. Defining the function as an ownvalue of f means that to use it, you only have to invoke the simplest part of the pattern matching machinery. For f[x_] the machinery, at least in principle, first has to check for an ownvalue of f, and then check downvalues for a match (more complicated). I believe this is behind the effect here.
• John, while you make a valid point, it could not possibly account for such dramatic time differences. This clearly looks like the effect of auto-compilation of Function[...] - based versions, in this particular case - as discussed in more detail in the Q/A linked by @Kuba. Commented Feb 10, 2022 at 20:35