22

I'm trying to find the fastest way of standardizing a matrix in Matlab (zero mean, unit variance columns). It all comes down to which is the quickest way of applying the same operation to all rows in a matrix. Every post I've read come to the same conclusion: use bsxfun instead of repmat. This article, written by Mathworks is an example: http://blogs.mathworks.com/loren/2008/08/04/comparing-repmat-and-bsxfun-performance/

However, when trying this on my own computer repmat is always quicker. Here are my results using the same code as in the article:

m = 1e5;
n = 100;
A = rand(m,n);

frepmat = @() A - repmat(mean(A),size(A,1),1);
timeit(frepmat)

fbsxfun = @() bsxfun(@minus,A,mean(A));
timeit(fbsxfun)

Results:

ans =

    0.0349


ans =

    0.0391

In fact, I can never get bsxfun to perform better than repmat in this situation no matter how small or large the input matrix is.

Can someone explain this?

Luis Mendo
  • 110,752
  • 13
  • 76
  • 147
user1337
  • 494
  • 3
  • 13
  • 1
    For me the bsxfun approach is always faster, also for bigger arrays. (Matlab 2014a) – Robert Seifert Feb 25 '15 at 15:22
  • For me (R2014b) bsxfun wins as well. What version of matlab do you use? – Wouter Kuijsters Feb 25 '15 at 15:24
  • I've tried it on both 2014a and 2014b. Could the OS used make a difference? I'm on OSX. – user1337 Feb 25 '15 at 15:26
  • OS X - R2014a: I can reproduce the results. Maybe the JIT Compiler has become more clever in the current version... You should still go with `bsxfun` though... – knedlsepp Feb 25 '15 at 15:27
  • 8
    In recent versions of MATLAB, `repmat` is a compiled function like `bsxfun` while it was an m-file in previous versions, so I expect a performance boost for it. On my system, there is equal performance to slightly better performance from `bsxfun` using your sample code (the difference is "in the wash" as I say). However, `bsxfun` still has the advantage of not allocating an extra array into memory. – TroyHaskin Feb 25 '15 at 15:30
  • @knedlsepp: I guess not since I've tried both a and b. But maybe OSX is the key? – user1337 Feb 25 '15 at 15:30
  • 14
    `bsxfun` is always better. No matter how long it takes :-P – Luis Mendo Feb 25 '15 at 15:38
  • I'm running Mac OS Mavericks with MATLAB R2013a. My timings are `0.0574` for `repmat` and `0.0288` for `bsxfun`. I can't reproduce your results. As such, I don't think it's an operating system issue. However, if we have to debate, I would choose `bsxfun` as well simply for readability and that you don't have to allocate any extra memory. It could be a variety of things that make the timing equal. It might be due to context switching of different programs. Do you have a lot of programs open right now? Did you also try doing a fresh reboot and running the tests again? – rayryeng Feb 25 '15 at 15:45
  • I can replicate the OP's results on OS X using a version I can't talk about. That article was written in 2008. `repmat` was nothing more than a regular M-file at the time. You can can still find this original M-code in current installs: type `edit toolbox/matlab/elmat/+matlab/+internal/+builtinhelper/repmat` in your command window. Many functions that started out as M-files have been turned into compiled native files over the years. Others have benefited from speedups due to recompilation for more modern hardware, better/newer libraries, and improved algorithms. – horchler Feb 25 '15 at 15:51
  • Win7-x64, `R2013b (repmat=0.0388, bsxfun=0.0368)` and `R2014b (repmat=0.0405, bsxfun=0.0372)` – CitizenInsane Feb 25 '15 at 15:52
  • 5
    To those timing, I recommend trying larger matrices, e.g., `m=1e5;` `n=1e3;` `A=rand(m,n);` at least. – horchler Feb 25 '15 at 15:53
  • 3
    @user89161: more likely it comes down to memory and cache. To do truly fair comparisons of this type across computers and OSes, we need to know about the CPUs involved, RAM, etc. – horchler Feb 25 '15 at 15:59
  • 1
    On the GPU (Tesla K20c/WIN64/R2014b), `bsxfun` is faster - with `m = 1e6`, I see 0.029 for `repmat` and `0.024` for `bsxfun`. – Edric Feb 26 '15 at 08:41
  • @user89161 Since it happens consistently, can you post results switching the order of the call? – krisdestruction Apr 19 '15 at 01:17

1 Answers1

13

Most of the advice you're reading, including the blog post from Loren, likely refers to old versions of MATLAB, for which bsxfun was quite a bit faster than repmat. In R2013b (see the "Performance" section in the link), repmat was reimplemented to give large performance improvements when applied to numeric, char and logical arguments. In recent versions, it can be about the same speed as bsxfun.

For what it's worth, on my machine with R2014a I get

m = 1e5;
n = 100;
A = rand(m,n);

frepmat = @() A - repmat(mean(A),size(A,1),1);
timeit(frepmat)

fbsxfun = @() bsxfun(@minus,A,mean(A));
timeit(fbsxfun)

ans =
      0.03756
ans =
     0.034831

so it looks like bsxfun is still a tiny bit faster, but not much - and on your machine it seems the reverse is the case. Of course, these results are likely to vary again, if you vary the size of A or the operation you're applying.

There may still be other reasons to prefer one solution over the other, such as elegance (I prefer bsxfun, if possible).


Edit: commenters have asked for a specific reason to prefer bsxfun, implying that it might use less memory than repmat by avoiding a temporary copy that repmat does not.

I don't think this is actually the case. For example, open Task Manager (or the equivalent on Linux/Mac), watch the memory levels, and type:

>> m = 1e5; n = 8e3; A = rand(m,n);
>> B = A - repmat(mean(A),size(A,1),1);
>> clear B
>> C = bsxfun(@minus,A,mean(A));
>> clear C

(Adjust m and n until the jumps are visible in the graph, but not so big you run out of memory).

I see exactly the same behaviour from both repmat and bsxfun, which is that memory rises smoothly to the new level (basically double the size of A) with no temporary additional peak.

This is also the case even if the operation is done in-place. Again, watch the memory and type:

>> m = 1e5; n = 8e3; A = rand(m,n);
>> A = A - repmat(mean(A),size(A,1),1);
>> clear all
>> m = 1e5; n = 8e3; A = rand(m,n);
>> A = bsxfun(@minus,A,mean(A));

Again, I see exactly the same behaviour from both repmat and bsxfun, which is that memory rises to a peak (basically double the size of A), and then falls back to the previous level.

So I'm afraid I can't see much technical difference in terms of either speed or memory between repmat and bsxfun. My preference for bsxfun is really just a personal preference as it feels a bit more elegant.

Sam Roberts
  • 23,951
  • 1
  • 40
  • 64
  • 1
    `bsxfun` still has the advantage of using up less memory, as it internally avoids data repetition, am I correct? I mean, give a reason to justify preference for `bsxfun`! :-) – Luis Mendo Apr 20 '15 at 20:33
  • @LuisMendo is it possible the re-implementation of `repmat` avoids the explicit memory copy? Is it possible it implements a "lazy" copy? I do not have access to recent Matlab versions... – Shai Apr 21 '15 at 05:44
  • 1
    @Shai Yes, that's what I meant with my question. It could be that `repmat` internally does some kind of optimization. I don't know. But I still prefer `bsxfun`! – Luis Mendo Apr 21 '15 at 08:45
  • @SamRoberts Thanks. Yes, I get the same behaviour here regarding memory. On the other hand, as for running time, `bsxfun` seems to be faster; [see Divakar's tests](http://stackoverflow.com/questions/29719674/comparing-bsxfun-and-repmat) – Luis Mendo Apr 22 '15 at 11:42
  • @SamRoberts And now there's more! http://stackoverflow.com/questions/29800560/bsxfun-on-memory-efficiency-with-relational-operations – Luis Mendo Apr 22 '15 at 14:42