6
$\begingroup$

According to this Wolfram Blog post, one can speed up Mathematica code by substituting numerical values as soon as possible.

How does one substitute before the main expression is evaluated? The following timings show the problem:

In[881]:= Timing[
 meanFirstPassage[N[.95* tenDesigner + (1. - .95) tenRandomUser], si, 
  gi]]

Out[881]= {0.46267, 4.3469}

In[880]:= Timing[
 meanFirstPassage[
  N[k* tenDesigner + (1. - k) tenRandomUser /. k -> .95], si, gi]]

Out[880]= {2.77539, 4.3469}

When I use the function above in Plot it reverts to the slow version in which the inner expression involving k is symbolically evaluated.

As noted by the response below, With does the trick. My main goal was to speed up Plot. Here is a self-contained example:

In[961]:= d = 100;
m1 = Table[RandomReal[], {d}, {d}];
m2 = N[Table[RandomInteger[], {d}, {d}]];

In[964]:= .95*m1 + (1. - .95)*m2; // AbsoluteTiming

Out[964]= {0.009045, Null}

In[965]:= k*m1 + (1. - k)*m2 /. k -> .95; // AbsoluteTiming

Out[965]= {0.059908, Null}

In[966]:= With[{k = .95}, k*m1 + (1. - k)*m2]; // AbsoluteTiming

Out[966]= {0.008615, Null}

In[967]:= Plot[Mean[k*m1 + (1. - k)*m2], {k, 0, 1}]; // AbsoluteTiming

Out[967]= {10.182414, Null}

In[968]:= Plot[With[{k = x}, Mean[k*m1 + (1. - k)*m2]], {x, 0., 1.}]; // AbsoluteTiming

Out[968]= {1.821481, Null}
$\endgroup$
1
  • 2
    $\begingroup$ I recommend reading the past discussions on performance tuning, Plot and With, and packed arrays, to better understand the way the system works. This may help you understand the reasons for the advice you cite, and then it will be clear how and when to apply it in different situations. $\endgroup$ May 4, 2012 at 7:08

2 Answers 2

3
$\begingroup$

I await a more complete question, but for now my best guess is:

a = 0.5; b = 0.2;

Do[N[.95*a + (1. - .95) b], {1*^5}] // AbsoluteTiming
{0.0670038, Null}
Do[N[k*a + (1. - k) b /. k -> .95], {1*^5}] // AbsoluteTiming
{0.3310190, Null}
With[{k = 0.95},
  Do[N[k*a + (1. - k) b], {1*^5}]
] // AbsoluteTiming
{0.0800046, Null}

With on the outside does the replacement before evaluating the Do loop.


Seeing your application I can recommend another considerable improvement. Even in your new form, With is inside Plot and reevaluated many times. If you force this to evaluate first it will be much faster. Here are three ways to do that, take your pick:

Plot[#, {x, 0., 1.}] & @ With[{k = x}, Mean[k*m1 + (1. - k)*m2]]

Plot[Evaluate @ With[{k = x}, Mean[k*m1 + (1. - k)*m2]], {x, 0., 1.}]

Plot[With[{k = x}, Mean[k*m1 + (1. - k)*m2]], {x, 0., 1.}, Evaluated -> True]

Please note two things:

  1. In each case above the global symbol x is not localized, as a result of the pre-evaluation. If it is possible to vary k directly, e.g. Plot[... {k, 0, 1}] you should probably do it.

  2. Because of the pre-evaluation you will find that the various lines are now styled in different colors. See this question and answers for an explanation. If you want uniform color lines add the option PlotStyle -> ColorData[1][1] to Plot.

$\endgroup$
11
  • 1
    $\begingroup$ +1, Now repeat after me: I'll not time code using AbsoluteTiming[], I'll not time code using ... :D $\endgroup$ May 4, 2012 at 0:05
  • 1
    $\begingroup$ @belisarius I don't know... I like the fact that AbsoluteTiming is measuring real time rather than cycles, and catching (nearly) all sources of slowdowns. There are also ramifications for parallel operations. $\endgroup$
    – Mr.Wizard
    May 4, 2012 at 0:25
  • 1
    $\begingroup$ @belisarius I think I'm still not understanding you. My point is that if someone slaps a Timing on a ParallelMap operation it's not going to give a useful result. AbsoluteTiming seems like a reasonable starting point for timing. Contrary to appearances I don't have a strong opinion about this, it's just that I don't understand your objection. $\endgroup$
    – Mr.Wizard
    May 4, 2012 at 0:58
  • 3
    $\begingroup$ I believe my point is trivial after this comments exchange: Just don't use AbsoluteTiming[] for measuring unless you have very good reasons to do it. And when you do, to do it well, you have to make sure that all other conditions in the subject machine/s and network/s are stable (which is very difficult to assert ... believe me, I earned my living for a few years just because I knew a few tricks to do the timming math in a certain environment). Timming[], while measuring only CPU secs, is infallible. $\endgroup$ May 4, 2012 at 1:10
  • 1
    $\begingroup$ This is an interesting comments thread, but I feel it is now too long to be useful. Performance measuring in parallel, multicore, multithreaded environments is a tricky business that you just can't successfully surmount with simple tools like the Timings offered natively by Mma, because if Timing results could be corrupted by a side effect of multithreading, AbsoluteTiming figures are always corrupted by any other piece of software running on your machine/s. $\endgroup$ May 4, 2012 at 12:06
0
$\begingroup$

Evaluating the expression with k before giving it to Plot works well for Mean, but not for the function that I'm actually using. In my application, the matrices are Markov models of a random user of a user interface and a perfect user of an interface. k represents the user's knowledge. I used Mean in the self-contained example to simplify the code I posted. But the function I'm plotting is actually this:

zeroRowCol[matrix_, rc_] := Module[{zmatrix = matrix},
   zmatrix[[rc]] = ConstantArray[0., Length@matrix];
   zmatrix[[ All, rc]] = ConstantArray[0., Length@matrix];
   Return[zmatrix]];

meanFirstPassage[matrix_, start_, start_] := 0.;

meanFirstPassage[matrix_, start_, goal_] :=

  Module[{One = ConstantArray[1., {Length@matrix}], 
    Id = N[IdentityMatrix[Length@matrix]]},
   (Inverse[Id - zeroRowCol[N[matrix], goal]].One)[[start]]
   ];

This function finds the mean number of transitions needed to go from a starting state in a Markov model to a given goal state. Here is the function call in my original post:

 meanFirstPassage[
  N[k* tenDesigner + (1. - k) tenRandomUser /. k -> .95], si, gi]]

If $k = 0$, the model acts like a user who chooses actions at random. At $k = 1$, the model acts like a perfect user who takes the best path to the goal and never makes a mistake. Evaluating the inner expression first in plot, such as in this expression:

Plot[#, {k, 0., 1.}] &@ 
  With[{x = k}, 
   meanFirstPassage[ N[x* tenDesignerc + (1. - x) tenRandomUserc], 
    sic, gic]]

is much slower than just using With directly in Plot, as in:

Plot[With[{x = k}, 
  meanFirstPassage[ N[x* tenDesignerc + (1. - x) tenRandomUserc], 
   sic, gic]], {k, 0.8, 1.}]
$\endgroup$
1
  • $\begingroup$ Hi Todd. Maybe you intended to add this to the question itself rather than being an answer? If so, you can always edit your question to incorporate additional text... $\endgroup$ May 6, 2012 at 3:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.