Re: Mathematica 20x slower than Java at arithmetic/special
- To: mathgroup at smc.vnet.net
- Subject: [mg115943] Re: Mathematica 20x slower than Java at arithmetic/special
- From: Oliver Ruebenkoenig <ruebenko at wolfram.com>
- Date: Wed, 26 Jan 2011 05:30:52 -0500 (EST)
On Mon, 24 Jan 2011, Leo Alekseyev wrote:
> Vivek, Oliver -- thanks for your input! My knowledge in using
> Compile[] is somewhat lacking (mostly, due to the fact that I was
> never able to get it to work well for me). In particular, I tried
> using Compile[] much in the same way that Vivek has suggested, but I
> neglected to use Evaluate[], which leads to a compiled function taking
> substantially longer. Is there a quick explanation for why Evaluate[]
> (or, in Oliver's example, a construct like
> With[{code=code},Compile[{...},code]] necessary?..
Let me try to do that.
Say I have
In[1]:= a = Sqrt[2]
Out[1]= Sqrt[2]
In[2]:= f[x_] := a^2*x
In[3]:= ?f
Global`f
f[x_] := a^2*x
So there is a global symbol a and f both with values. We can call the function
f with a parameter
In[4]:= f[1]
Out[4]= 2
Let us look at how this is evaluated
In[5]:= f[1] // Trace
2 2 1
Out[5]= {f[1], a 1, {{a, Sqrt[2]}, Sqrt[2] , 2 , 2}, 2 1, 2}
We see the call f[1], then on the right hand side x is replaced by the
parameter 1, the evaluator now looks up what a is and computes a^2, does the
multiplication and returns the result.
This means that every time you call f with any parameter the lookup of a and
the computation of a^a needs to be done. This can be expensive. Now, there are
several ways to deal with this. Depending on the circumstances.
Let us look at the Evaluate first.
The above definition of f can be written as such (FullForm)
(*SetDelayed[f[x_],a^2*x]*)
Set*Delayed* (:=) has
In[6]:= Attributes[SetDelayed]
Out[6]= {HoldAll, Protected, SequenceHold}
Of interest are the Hold_XY_ attributes. This means the when you specify f, the
right hand side is *not* evaluated. (In contrast to Set (=)).
So we can force the evaluation of the right hand side with
In[7]:= SetDelayed[f2[x_], Evaluate[a^2*x]]
In[8]:= ?f2
Global`f2
f2[x_] := 2*x
Then
In[9]:= f2[1] // Trace
Out[9]= {f2[1], 2 1, 2}
is much cheaper to compute.
You can also write the above as:
f2[x_] := Evaluate[a^2*x]
OK, now a different approach.
In[10]:= ?With
So With does *replacement* not evaluation. This is different but also valuable.
Say we have
In[11]:= g[u_, i_] := a^2*u[[i]]
In[12]:= u = {1, 2, 3};
In[13]:= g[u, 2] // Trace
2
Out[13]= {{u, {1, 2, 3}}, g[{1, 2, 3}, 2], a {1, 2, 3}[[2]],
2 1
> {{a, Sqrt[2]}, Sqrt[2] , 2 , 2}, {{1, 2, 3}[[2]], 2}, 2 2, 4}
If we now apply the above Evaluate technique we get
Clear[g]
In[15]:= g[u_, i_] := Evaluate[a^2*u[[i]]]
Part::pspec: Part specification i
is neither an integer nor a list of integers.
We get a message but what is worse is
In[16]:= ?g
Global`g
g[u_, i_] := 2*{1, 2, 3}[[i]]
in the function g u has been replaced by the global u.
This can be circumvented with
In[17]:= Clear[g]; With[{a = a}, g[u_, i_] := a^2*u[[i]]]
In[18]:= ?g
Global`g
g[u$_, i$_] := Sqrt[2]^2*u$[[i$]]
Now, a has been replaced but not evaluated and u is not replaced.
In[20]:= Clear[g]; With[{a = a}, g[u_, i_] := a^2*u[[i]]] // Trace
2
Out[20]= {With[{a = a}, g[u_, i_] := a u[[i]]], {a, Sqrt[2]},
2
> g[u$_, i$_] := Sqrt[2] u$[[i$]], Null}
I think of With as a method to inject code into functions that have some kind
of Hold attribute.
So, strictly speaking in the code I send the Evaluate would have been
sufficient.
Another scenario where the above techniques come in handy is for conditional
evaluation.
In[21]:= Attributes[If]
Out[21]= {HoldRest, Protected}
Then you have the option to inject code into the HoldRest part of If
I hope this was useful?
Oliver