# Functions, Speed, and Evaluate[]

This is a question about how Mathematica works, rather than how to solve a specific instance of an issue, so apologies in advance if this isn't admissible for this StackExchange. I've noticed some weird behaviour about how quickly code runs, and I'd like to understand why one case is so much faster than another, and if there's a way of tweaking the latter to make it faster.

Everything is done on notebooks ran on WolframCloud with a free basic account, in case that matters.

Consider some generic function and check how fast it runs

egg[t_] := Exp[1-Log[1+Sqrt[t]]]

Timing[Table[egg[t], {t, 1, 10000}]][[1]]
Timing[Table[egg[t] //Evaluate, {t, 1, 10000}]][[1]]

Out= 0.510792
Out= 0.07173


Adding an explicit Evaluate makes it almost 8x faster! The same is true if we plot it rather than make a table (70ms vs 14ms).

If I define a new function that does the evaluation, nothing changes, it's still slow unless I add an expilcit //Evaluate inside of table

eval[func_,t_] := func[t] //Evaluate

Timing[Table[eval[egg,t], {t, 1, 10000}]][[1]]
Timing[Table[eval[egg,t] //Evaluate, {t, 1, 10000}]][[1]]

Out= 0.503777
Out= 0.071542


What's happening here? Is there a way of defining a function such that it runs quickly without adding an Evaluate to it every time it's called?

It's the automatic simplification of the expression that spends the additional time. Compare the following two definitions of a function:

egg[t_] := Exp[1 - Log[1 + Sqrt[t]]]
egg2[t_] := Exp[1 - Log[1 + Sqrt[t]]] // Evaluate;

Definition[egg]
(* egg[t_] := Exp[1 - Log[1 + Sqrt[t]]] *)

Definition[egg2]
(* egg2[t_] := E/(1 + Sqrt[t]) *)


And compare also the evaluation processes:

Trace[egg[1]]
(* {egg[1],Exp[1-Log[1+Sqrt[1]]],{{{{{Sqrt[1],1},1+1,2},Log[2]},-Log[2]},
1-Log[2]},Exp[1-Log[2]],E^(1-Log[2]),E/2} *)

Trace[egg2[1]]
(* {egg2[1],E/(1+Sqrt[1]),{{{Sqrt[1],1},1+1,2},1/2,1/2},E/2,E/2} *)


As you can see, the definition of egg2 already contains the simplified form, while egg doesn't. Therefore, at every evaluation of egg, the whole simplification process will occur (you can see that the Trace is longer for egg), and the overall time will be longer. By using Evaluate, you force the simplification (evaluation) to happen already at the time of defining your function.

If you don't like adding Evaluate to the definition of the function, you can use Set instead of SetDelayed. Just be careful that you haven't given t any value beforehand (that's why I used t\$ in the definition instead).

egg3[t$$_] = Exp[1 - Log[1 + Sqrt[t$$]]];

RepeatedTiming[Table[egg[t], {t, 1, 10000}]] // First
(* 0.484119 *)

RepeatedTiming[Table[egg2[t], {t, 1, 10000}]] // First
(* 0.0800574 *)

RepeatedTiming[Table[egg3[t], {t, 1, 10000}]] // First
(* 0.079735 *)


Note that there is not much performance difference between the three definitions if you are interested only in the numerical instead of symbolic results.

RepeatedTiming[Table[egg[N[t]], {t, 1, 10000}]] // First
(* 0.0281943 *)

RepeatedTiming[Table[egg2[N[t]], {t, 1, 10000}]] // First
(* 0.0271799 *)

RepeatedTiming[Table[egg3[N[t]], {t, 1, 10000}]] // First
(* 0.027255 *)


As for your function eval: It doesn't really do anything, because it doesn't know your function beforehand.

Definition[eval]
(* eval[func_, t_] := func[t] *)


Therefore, calling eval[egg, t] will trigger reevaluation of egg every time.

Also note that Table has the HoldAll attribute. If you use Evaluate directly inside Table, then you bypass this, and the evaluation of your function happens only once. For better understanding, I suggest reading the tutorial Evaluation of Expressions.

RepeatedTiming[Table[egg[t] // Evaluate, {t, 1, 10000}]] // First
(* 0.0760611 *)

• Thanks for the thorough answer! I've been using Mathematica one-and-off for a decade and I've forgotten what little of its foundations I ever knew. That tutorial looks very promising to solve my unknown unknowns. Commented Mar 8 at 18:44