Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

I am able to evaluate a held expression that I have put on a list by:

ReleaseHold[expressionList[[1]]]

and

expression1 := ReleaseHold[expressionList[[1]]];
expression1

Using expression1 is more efficient than ReleaseHold[expressionList[[1]]], but I loose the ability to use it in Map (or so I think). I would like to be able to use the held expression directly from a list, i.e. expressionList[[1]] .

Any suggestions?

A test harness follows. You can change the expressions being tested see AddExpression[1];, which is currently 1 to Pause[$TimeUnit], Range[1, 1000], ... .


SetAttributes[AddExpression, HoldAll];
AddExpression[expression_] := AppendTo[expressionList, Hold[expression]];

EvaluateExpressions[] :=
 (
  expression1 := ReleaseHold[expressionList[[1]]];
  expression2 := ReleaseHold[expressionList[[2]]];

  totalRunTimeDo = AbsoluteTiming[
     Do[(expression1; expression2;), {iterations}];
     ][[1]];

  Print["Do      total: ", totalRunTimeDo, "   per iteration: ", 
   totalRunTimeDo/ iterations];

  (* -------------------------------------------------*)

  totalRunTimeDoMap = AbsoluteTiming[
     Do[Map[ReleaseHold[#] &, expressionList], {iterations}];
     ][[1]];

  Print["DoMap   total: ", totalRunTimeDoMap, "   per iteration: ", 
   totalRunTimeDoMap/ iterations];
  )

iterations = 10000000;
expressionList = {};
AddExpression[1];
AddExpression[1];
EvaluateExpressions[];

Results:

Do      total: 12.526729   per iteration: 1.2526729*10^-6
DoMap   total: 19.018290   per iteration: 1.9018290*10^-6

Obviously, you can see why I want to pull it directly from the list.

Background

The above test harness was boiled down from something I was using to benchmark code.

iterations = 10000000;
expressionList = {Hold[1], Hold[1]};

I started here:

expressionTimingsByIteration =
  Map[Map[AbsoluteTiming[ReleaseHold[#]][[1]] &, expressionList] &,
   Range[1, iterations]];
iterationsTimingsByExpression = Transpose[expressionTimingsByIteration];

Refactored to this:

iterationsTimingsByExpression =
  Map[
   (
     expression := ReleaseHold[#];
     Map[AbsoluteTiming[expression][[1]] &, Range[1, iterations]] 
     ) &
   , expressionList];

However evaluating code whose AbsoluteTiming < $TimeUnit is prone to error. Which I refactored for those cases to:

totalTimingByExpression =
  Map[
   (
     expression := ReleaseHold[#];
     AbsoluteTiming[Do[expression, {iterations}]][[1]]
     ) &, expressionList];
totalTimingByExpression
share|improve this question
1  
Related question: stackoverflow.com/questions/6254538/… –  Leonid Shifrin Mar 9 '12 at 10:40
    
@LeonidShifrin Sorry about the duplicate post. Is there away to search stackoveflow and mathematica.se at the same time? Or are there plans to move/link the Mathematica posts on stackoverflow to mathematica.se? While having a separate mathematica.se makes sense it seems. However having two sites for Mathematica only increases the likely hood of future duplicate questions. –  mmorris Mar 9 '12 at 14:57
    
There is no reason to be sorry. It is not always easy to find what you need, and in any case the question is not duplicate because SO and Mathematica SE are different sites. Eventually some questions get migrated from SO to this site. Right now, this site is by far the most active Mathematica resource on SE, while the SO Mathematica tag remains mostly a repository of some already answered questions. –  Leonid Shifrin Mar 9 '12 at 15:28
    
@LeonidShifrin Hmmmm, I am trying to parse you statement, "SO and Mathematica SE are different sites". Sites as in databases, but not general purpose? I thought, please correct me if I am wrong (< 1 year on SO and < 1 moth here), SO is general purpose for questions(discussions) on topics that don't have a specialized SE site. In theory the purpose of SO w/[mathematica] == Mathematica SE, where Mathematica SE is specific to the Mathematica domain which allows for categorization via tags within that domain. –  mmorris Mar 9 '12 at 15:53
1  
Within SO Mathematica tag, only programming questions were on topic, strictly speaking. Also, it was just one tag within a much larger SO infrastructure. The purpose of the two sites is similar, but they are different sites in terms of infrastructure, moderation, rules, etc. You can read discussions which were accompanying the SE Mathematica proposal on Area51, to better see the difference - it was extensively discussed there. –  Leonid Shifrin Mar 9 '12 at 16:21
add comment

2 Answers 2

up vote 6 down vote accepted

What costs you so much time is your unnecessary packaging of ReleaseHold into an anonymous function. ReleaseHold[#]& is semantically identical to ReleaseHold but much more time expensive. If you replace it with ReleaseHold, the map version is only slightly slower than the direct version (and I guess that's because of the Map logic which has to work with lists of arbitrary length).

Here's what I get after removing the unnecessary anonymous function:

Do      total: 22.849438   per iteration: 2.2849438*10^-6
DoMap   total: 24.159825   per iteration: 2.4159825*10^-6
share|improve this answer
    
Thanks for that, I will pay more attention to not using anonymous functions. That narrowed the performance difference significantly. Do total: 12.386823 DoMap total: 19.275236 DoMapDirect total: 13.860921 I had a previous version that used two Maps instead of a Do and a Map MapMap total: 21.982573. That is when I learned to use Do in place of Map when just iterating over a value not used in the loop. –  mmorris Mar 9 '12 at 18:29
add comment

I think that, first, there is a simpler way to do what you intend, and second, the benchmarks and timing results will significantly depend on the total length of the expression list. For small expression lists, a large part of the total running time is not related to expressions evaluation, but to the overall overhead of things like CompoundExpression etc.

First, here is how I'd do this:

ClearAll[$expressionList, addExpression, clearExpressions];
$expressionList = Hold[];
SetAttributes[addExpression, HoldAll];
addExpression[expression_] :=
     $expressionList = Append[$expressionList, Unevaluated[expression]];
clearExpressions[] := $expressionList = Hold[];

Basically, the idea is to store all expressions in unevaluated form in a single Hold (you can use HoldComplete if you wish). Then, evaluation of a given held expression is just a matter of extracting the relevant part, while evaluating them all is just as easy as applying ReleaseHold to $expressionList.

I will now populate $expressionList with 1000 unevaluated expressions:

iterations = 20000;
clearExpressions[];
Do[With[{i = i}, addExpression[i*i]], {i, 1, 1000}];

(I had to reduce the number of interations accordingly). Now, sequential evaluation is somewhat faster (about 1.5 times) than in your constructs, both for loops and Scan (can not use Map here however, won't evaluate inside held expression by itself)

AbsoluteTiming[
    Do[Do[$expressionList[[i]],{i,1,Length[$expressionList]}],{iterations}];][[1]]
AbsoluteTiming[Do[Scan[#&,$expressionList],{iterations}];][[1]]

(* 
  ==> 11.3134766
  ==> 10.2519531
*)

However, evaluating expressions "wholesale" is even much faster:

AbsoluteTiming[Do[ReleaseHold@$expressionList;, {iterations}];][[1]]

(* 
  ==> 3.2216797
*)
share|improve this answer
    
I like this solution. The timing numbers are impressive too. I applied your solutions to my original test, the results are: 13.047372, 9.770802, and 5.494147. The second implementation, Do Scan, seems most applicable to my intended application, compared to my contrived example above. I could have had my cake and eat it too if Scan only returned results of each call like Map does. –  mmorris Mar 9 '12 at 18:14
1  
@mmorris If you use Map, then you could just as well use my last suggestion ReleaseHold@$expressionList, couldn't you? Or do you want to evaluate only some subset of elements in your held list, and collect the results of that? –  Leonid Shifrin Mar 9 '12 at 18:45
    
The example code above is intended to benchmark benchmarking code. Original benchmark code: expressionTimingsByIteration = Map[Map[AbsoluteTiming[ReleaseHold[#]][[1]] &, expressionList] &, Range[1, iterations]]; iterationsTimingsByExpression = Transpose[expressionTimingsByIteration]; Refactored to this: iterationsTimingsByExpression = Map[ ( expression := ReleaseHold[#]; Map[AbsoluteTiming[expression][[1]] &, Range[1, iterations]] ) & , expressionList]; iterationsTimingsByExpression ... –  mmorris Mar 10 '12 at 1:52
    
... However evaluating code whose AbsoluteTiming < $TimeUnit is prone to error. Which I refactored for those cases to: totalTimingByExpression = Map[ ( expression := ReleaseHold[#]; AbsoluteTiming[Do[expression, {iterations}]][[1]] ) &, expressionList]; totalTimingByExpression Which I was looking to eliminate what celtschk pointed out. –  mmorris Mar 10 '12 at 2:05
    
I'll post those in my question so they format better. –  mmorris Mar 10 '12 at 2:20
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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