19

I have noticed that the first time I run a script, it takes considerably more time than the second and third time1. The "warm-up" is mentioned in this question without an explanation.

Why does the code run faster after it is "warmed up"?

I don't clear all between calls2, but the input parameters change for every function call. Does anyone know why this is?


1. I have my license locally, so it's not a problem related to license checking.

2. Actually, the behavior doesn't change if I clear all.

nbro
  • 15,395
  • 32
  • 113
  • 196
Stewie Griffin
  • 14,889
  • 11
  • 39
  • 70

4 Answers4

9

One reason why it would run faster after the first time is that many things are initialized once, and their results are cached and reused the next time. For example in the M-side, variables can be defined as persistent in functions that can be locked. This can also occur on the MEX-side of things.

In addition many dependencies are loaded after the first time and remain so in memory to be re-used. This include M-functions, OOP classes, Java classes, MEX-functions, and so on. This applies to both builtin and user-defined ones.

For example issue the following command before and after running your script for the first run, then compare:

[M,X,C] = inmem('-completenames')

Note that clear all does not necessarily clear all of the above, not to mention locked functions...

Finally let us not forget the role of the accelerator. Instead of interpreting the M-code every time a function is invoked, it gets compiled into machine code instructions during runtime. JIT compilation occurs only for the first invocation, so ideally the efficiency of running object code the following times will overcome the overhead of re-interpreting the program every time it runs.

Bernhard
  • 3,619
  • 1
  • 25
  • 50
Amro
  • 123,847
  • 25
  • 243
  • 454
  • Thanks Amro! Do you know if the script will run faster if only the necessary programs are in the P-code buffer, and not all things used in other function calls that I'm not interested in? I.e. should I `clear all` before benchmarking a specific script? I haven't noticed a significant different, but I haven't "filled up" the buffer either... – Stewie Griffin Jun 02 '13 at 09:00
  • 1
    Amro, Unless your code is quite small, JIT compilation is the primary effect in speeding up code in runs after the first - loading functions is not as important. Also, JIT compilation doesn't only occur on the first invocation - MATLAB will sometimes only JIT compile portions of your code on the first run, and other portions later. @RobertP., if you need to benchmark code, the `timeit` function linked to in the question you referenced is the best way to do that. Among other things, it handles all the "warm-up" issues you describe for you. – Sam Roberts Jun 03 '13 at 12:48
  • @SamRoberts: I agree, that is certainly the promise of JIT compilation. If a piece of code is repeatedly called, we expect the JIT compiler to detect this "hot-spot" during runtime, and optimize it into direct machine code. Hopefully even if not all in the same run. As for functions loading, I think it is more of an issue for MEX-functions where the overhead of loading external dependencies is higher. – Amro Jun 03 '13 at 13:32
  • @RobertP.: I doubt clearing the memory will make things run any faster.. Even if it did, you should never rely on the details of a language implementation to optimize your program performance. Always write idiomatic code – Amro Jun 03 '13 at 13:38
2

Matlab is interpreted. If you don't warm up the code, you will be losing a lot of time due to interpretation instead of the actual algorithm. This can skew results of timings significantly.

Running the code at least once will enable Matlab to actually compile appropriate code segments.

nbro
  • 15,395
  • 32
  • 113
  • 196
Marc Claesen
  • 16,778
  • 6
  • 27
  • 62
1

Besides Matlab-specific reasons like JIT-compilation, modern CPUs have large caches, branch predictors, and so on. Warming these up is an issue for benchmarking even in assembly language.

Also, more importantly, modern CPUs usually idle at low clock speed, and only jump to full speed after several milliseconds of sustained load.

Intel's Turbo feature gets even more funky: when power and thermal limits allow, the CPU can run faster than its sustainable max frequency. So the first ~20 seconds to 1 minute of your benchmark may run faster than the rest of it, if you aren't careful to control for these factors.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
0

Another issue not mensioned by Amro and Marc is memory (pre)allocation.
If your script does not pre-allocate its memory it's first run would be very slow due to memory allocation. Once it completed its first iteration all memory is allocated, so consecutive invokations of the script would be more efficient.

An illustrative example

for ii = 1:1000
    vec(ii) = ii; %// vec grows inside loop the first time this code is executed only
end
Community
  • 1
  • 1
Shai
  • 111,146
  • 38
  • 238
  • 371
  • 3
    @Shai is right about preallocation, however I'd like to disagree about the consequences. Code that does something like this is *badly written*. Warming up this kind of code will make it faster the next time but in this case *the algorithm is bad*. This is why I feel that, in this case, warming up actually skews timings in the advantage of poor code and masks bad implementations! – Marc Claesen Jun 02 '13 at 09:24
  • @Shai Preallocation is a good thing, but it has the same effects on the first and subsequent invocations. In your example, `vec` does not grow only the first time the code is executed, it grows every time the code is executed. – Sam Roberts Jun 03 '13 at 12:53
  • @Shai, sorry, that's not quite true as this is within a script, so `vec` would already exist if you ran the script again. If it were in a function, it grow each time the code was executed, not just the first. – Sam Roberts Jun 03 '13 at 12:54
  • @SamRoberts - it really depends on how you init `vec`. If you use the loop to init `vec` in a script it will grow in the first time the script runs and it will not change size for the second (and all consecutive) runs. – Shai Jun 03 '13 at 12:57