5
$\begingroup$

I have a collection of functions (Modules) and I wish to find out which sub-routines inside the code take up most time. I have done this by wrapping individual code blocks with

Print[Row[{"comment: ", AbsoluteTiming[ --- code --- ]}]];

This is tedious to do, and it is also tedious to remove these code blocks again once I am done with my optimization.

What I am looking for is a routine that can be turned on and off (e.g. by setting an Option), something like

timeIt[ --- code --- ,"comment"];

that can be left in place - it will simply execute the code when the Option is set to not report timing, and it will execute the code and return the timings when it is turned on.

So far, when I tried to put my Print[...] construct into an external module it worked, but it always returned a time of 0.

Here is the code that I have tried:

Clear[timeIt];
Options[timeIt] = {debugTiming -> True};
SetAttributes[timeIt, HoldFirst];
timeIt[f_, comment_: "", OptionsPattern[]] := 
  If[OptionValue[debugTiming] === True, 
   Print @ Row[{comment,": ",First@AbsoluteTiming[ReleaseHold@f]}], 
   ReleaseHold@f];

Any suggestions of how to do this properly?

$\endgroup$
  • $\begingroup$ Before I dig into this have you seen and understood this Q&A?: (7768) $\endgroup$ – Mr.Wizard Dec 12 '14 at 17:05
  • $\begingroup$ Also, are you having any problems with your implementation of timeIt? Are you requesting a code review? The first thing I note is that you do not need ReleaseHold as there is no actual Hold imposed by the use of HoldFirst. $\endgroup$ – Mr.Wizard Dec 12 '14 at 17:07
  • $\begingroup$ @Mr.Wizard I have seen the Q&A you linked to. However, my program is longish and I don't want the whole thing profiled, just sub-parts of the code. $\endgroup$ – Thomas Dec 12 '14 at 17:09
  • $\begingroup$ @Mr.Wizard I first tried without any "Hold". My impression was that the code wrapped by timeIt is executed before being passed to timeIt, and there was nothig left to be executed inside timeIt, thus a timing of 0 is returned. Then I tried all kinds of "Hold" (HoldAllComplete etc) but it did not change the outcome. $\endgroup$ – Thomas Dec 12 '14 at 17:12
  • $\begingroup$ What I meant is that you can use e.g. SetAttributes[fn, HoldFirst] and then fn[x_] := AbsoluteTiming[x] as the argument of fn will be passed unevaluated to AbsoluteTiming and then evaluated normally; you do not need an additional release. Again I ask: are you having any functional problems with your implementation? $\endgroup$ – Mr.Wizard Dec 12 '14 at 17:16
3
$\begingroup$

Here is what I would do:

ClearAll[timed];
SetAttributes[timed, HoldFirst];
timed[expr_, timeItQ_] := Module[
  {
   timing,
   time,
   result
   },
  Switch[ timeItQ,
   True, (
     timing = Timing[ expr];
    Sow[ First @ timing ]; (* maybe use tags like SymbolName @ expr *)         
    result = Last@timing;
    ),
   False, result = expr
   ];
  result
  ]

Clear[myfunction];
myfunction[x_] := Table[ "Do Something";, {x}]

ClearAll[main];
Options[main] =
  {
   "timeMyFunction" -> True
   (* options for other functions *)
   };

main[opts : OptionsPattern[{main}]] := Module[
  {},
  timed[myfunction[10000000], OptionValue["timeMyFunction"] ]
  (* rest of code *)
  ]

Then you can do

Reap [ main[] ][[2]]

(* {{2.823618}} *)

I thought you wanted to time different functions and wanted to have some options to declare for which at the part of the main program. That is how this example is constructed so the Reap will collect the timings. One can use tags for the Sow expressions of course if the timings are to be collected for more than one function at any run.

To make this nicer you might put a timedMyFunction for all the possible function candidates in the main part so when different parameters are given, so that it is nicer to read.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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