Loren on the Art of MATLAB

July 12th, 2006

What Are You Really Measuring?

I had an interesting encounter with a colleague, Bob, last week. We were talking about timing some calculations and we realized that the written code was actually measuring something different. Here's a small cautionary tale along with a neat factoid about vectors and for loops in MATLAB.

Contents

The Story

The story Bob and I started off with is less interesting than where we ended up. After looking at some code, I mentioned to Bob that using square brackets when there was no real concatenation was sometimes expensive in MATLAB. You'll see this as a message from mlint. We pared the code in question down to this:

dbtype forsquare
1     %% forsquare
2     for j = 1:ntimes
3         for k=[1:theEnd] 
4             k; 
5         end
6     end

and set out to compare it with this code:

dbtype forparen
1     %% forparen
2     for j = 1:ntimes
3         for k=(1:theEnd) 
4             k; 
5         end
6     end

Notice that the only difference between these M-files is the way format of the expression, the first one using square brackets and the second using parentheses to group the expression. We'll now set out to time the two M-files.

Prep

clc, clear
ntimes = 10;
ntotal = 1e7;
theEnd = ntotal/ntimes;

[], measure with tic-toc

tic, forsquare, toc
Elapsed time is 42.060557 seconds.

(), measure with tic-toc

tic, forparen, toc
Elapsed time is 0.173959 seconds.

[], measure with cputime

t = cputime; forsquare, cputime-t
ans =
   41.2794

(), measure with cputime

t = cputime; forparen, cputime-t
ans =
    0.1702

Explanation

I am timing these on my home laptop with MATLAB R2006a. I have 512 MB of RAM and 1.70 GHz processor. I am performing the timings with both tic toc and cputime tic - toc is easier, but I am not being too careful about what else is running on my machine so I am also using cputime. As you can see, it doesn't make much difference in this case. The construct with the [] is noticeably slower. Why is that?

In forsquare, the for loop expression delimited by the square brackets [] first constructs the full array inside the brackets in preparation for concatenating the array. In this case, there is nothing else being tacked on, so we create the full vector, ntotal elements in length. However, in the case of forparen, the parentheses () are used solely for grouping and MATLAB recognizes the expression as one it can expand during the execution of the for loop. In forsquare we create a large vector and that allocation dominates the time of the operation. In case you don't believe me that expressions in for loops are expanded as they are used, when possible, here's some code to look at:

dbtype forinf
1     %% forinf
2     for j = 1:Inf
3        if j > 17
4            break
5        end
6     end

Notice that the end of the for expression is Inf ! There's no way I have enough memory to create this vector, and yet the code runs just fine:

tic, forinf, toc
Elapsed time is 0.000061 seconds.

Remarks

Timing is a delicate matter. Be sure you know what exactly you are timing when you make comparisons. Do you have any timing war stories to share?


Published with MATLAB® 7.2

9 Responses to “What Are You Really Measuring?”

  1. Loren replied on :

    Sorry for the truncation on the first release of this article.

    –Loren

  2. Steve Amphlett replied on :

    I had often wondered about the use of 1:Inf in a for loop. So now I know. Thanks. Which category does

    for k=1:theEnd

    fit? Is it square or paren?

    - Steve

  3. Loren replied on :

    Steve-

    Regular colon notation in for loops fits into the paren behavior.

    –Loren

  4. Sia replied on :

    Well it’s not exactly a timing issue but I have something to tell about my experience with matlab performance. I brought two of my habits from FORTRAN programming to this amazing Matlab world.

    the first is most of the times it improves the speed dramatically if you use * instead of / if the Denominator is a constant for example instead of a/2 most of the times it helps if you use a*.5 (it makes sense but most of the times we forget to use it)

    And one myth is it is better to write your if statements like this ‘if 1==a’ instead of ‘if a==1′ however I still don’t know if it is just a myth or not

    And probably everyone knows how to use && ;)

  5. Loren replied on :

    Sia-

    I don’t know which version of MATLAB you are using or how you are doing your timing. I used R2006a and ran this script:

    N = 1;
    M = magic(2000);
    K = zeros(size(M));
    t = cputime;
    for num = 1:N,
        K = M*0.5;
    end
    cputime-t
    t = cputime;
    for num = 1:N,
        K = M/2.0;
    end
    cputime-t
    

    and I basically see no difference in scalar multiplication vs. division.

    I similarly tried timing 1==a vs. a==1 and found no difference. I am using a Windows XP laptop.

  6. Sia replied on :

    Loren, It’s not what I meant. you should use / instead of the *. I use R14 (no service pack) on a 2.4 XP laptop 1Gig Ram, please replace your code with this

    clc
    clear all;
    N = 1;
    M = magic(2000);
    K = zeros(size(M));
    t = cputime;
    for num = 1:N,
    K = M*.272423;
    end
    cputime-t
    t = cputime;
    for num = 1:N,
    K = M/3.67076;
    end
    cputime-t

    on mine it shows 30% better performance for * , however it is really dependant on the case you are solving. sometimes I see 300% improvement.

    for the a==1 case I wrote this

    clc;
    clear all;

    t=cputime;

    for i=1:20000000
    for j=i:5
    if 2==j
    alpha=200;
    end
    end
    end
    cputime-t

    clear all;
    t=cputime;
    for i=1:20000000
    for j=i:5
    if j==2
    alpha=200;
    end
    end
    end
    cputime-t

    the difference was just 2 sec in 15 sec, but at least showed that the 1==a case was faster

    have a good week

  7. Loren replied on :

    Sia-

    I fixed my typo in the comment above. I had compared /2 and *.5 in my timings and saw little difference. But I see some difference with your first set of numbers (but much smaller than the 30% you are seeing). Thanks for the interesting test cases.

    There are obvious ways to vectorize some codes like these, but you can’t always do so.

    –Loren

  8. Srini replied on :

    Constructs like if(1==a) above, where the literal is to the left of the “==” help prevent accidents like (if(a=1)). Faster in the sense that it’ll save bug fixing time :)

  9. Daniel replied on :

    Interesting. The fact that Matlab does funky stuff in the for-loop explains why some stuff I tried to do once didn’t work.

Leave a Reply

Wrap code fragments inside <pre> tags, like this:

<pre class="code">
a = magic(3);
sum(a)
</pre>

If you have a "<" character in your code, either follow it with a space or replace it with "&lt;" (including the semicolon).


Loren Shure works on design of the MATLAB language at The MathWorks. She writes here about once a week on MATLAB programming and related topics.

  • Jun: I totally can not believe it, Loren. You are really helpful. Thank you so much, MATLAB master!
  • Loren: Wow folks- Always lots of interest when there’s a quickie to try out! I will only make 2 general...
  • Loren: Jun- ismember is your friend here: >> [aa,ind] = ismember(Array2,Arra y1) aa = 1 1 1 1 1 1 1 ind = 1 2 1 4 4 3...
  • Dan: I like the first way better than the second way. Combining the arrays into one and running any is nice, although...
  • James Myatt: How about I = (a == 0 | b == 0); a(I) = []; b(I) = [];
  • Tunc: Hello Loren, love your blog because of such inspiring and challenging comments to such ’small’...
  • Pekka Kumpulainen: Here is my tradeoff. I usually want to keep the original variables as they are most probably...
  • Iain: Followup: Of course, to allow NaNs (counting them as non-zero): mask = (a~=0) & (b~=0); The mask says “a...
  • Matt Fig: I would usually go with something like this: y = a&b; x = a(y); y = b(y); But I was surprised to find...
  • kk: c=all([a;b]) a(c) a(b)

These postings are the author's and don't necessarily represent the opinions of The MathWorks.