-1
\$\begingroup\$

I have the following code in matlab and since the matrices are huge it takes more than one hour. I wanted to know where the bottle neck is and if there is any way to optimize this and make it faster.

...

for j = 1:1:2500

    if (getappdata(h, 'canceling'))     %Cancel waitbar
        break;
    end

    waitbar((j * dz)/max, h, 'Calc.');

    H = (stp * H')';
    Intn((j)+1, :) = H .* conj(H);

    xs2 = - lp(j);
    xe2 = lp(j);

    for m = 1:1:4097

        if (x(m) < xs) && (x(m) >= start)
            n(m) = 1;
        elseif (x(m) > xe) && (x(m) <= ende)
            n(m) = 1;
        elseif (x(m) > xs2 && x(m) < xe2)
            n(m) = 1;
        else
            n(m) = 0;
        end
    end

    in = [(k0^2 * (n.^2 - nbar^2) - 2 * 1/stepx^2), (ran2 + 1/stepx^2 - ran2), (ran2 + 1/stepx^2 - ran2)];
    P = sparse(zl, sp, in);

    N = P/(2*k) + P^2/(4*k^3);  
    N = sparse(N);

    D = eye(length(H)) + 3*P/(4*k^2) + P^2/(16*k^4); 
    D = sparse(D);

    stp = (D + 1i*dz/2 * N)/(D - 1i*dz/2 * N);

end

where dz,xs,xe,xs2,xe2,max are Inetegers. H, x and lp are arrays of type double with 4000, 4000 and 10,000 elements. Intn is a two dimentional matrix 10,000 x 4000.

\$\endgroup\$
5
  • \$\begingroup\$ First use the profiler to measure the time each line of code takes, then get back to us with the actual operation you want to speed up. \$\endgroup\$
    – Adriaan
    Apr 25, 2017 at 10:23
  • \$\begingroup\$ My suggestion would be profiler as well, but also get rid of the waitbar stuff - why waste time on that especially on such a fine step. Also replace the for m loop with logical indexing. \$\endgroup\$
    – Adrian
    Apr 25, 2017 at 10:26
  • \$\begingroup\$ I am trying to use the profiler but it takes so long that I thought maybe there is an obvious time consuming action that I dont know about. @Adriaan Everything is allocated before the loop. \$\endgroup\$
    – dieKoderin
    Apr 25, 2017 at 12:18
  • 3
    \$\begingroup\$ You should change the title to describe what your code does, not what you want out of the review, as the current title describes basically every MATLAB question asked here on Code Review. Have a look at our help center and "How do I ask a good question?". \$\endgroup\$
    – Graipher
    Apr 25, 2017 at 13:37
  • \$\begingroup\$ You can use the profiler. Just change for i = 1:1:2500 to for i = 1:50, or even for i = 1:5. The bottleneck should be the same place regardless of the number of iterations. \$\endgroup\$ Aug 29, 2017 at 6:00

2 Answers 2

7
\$\begingroup\$

Profiler

I want to know where the bottle neck is... Use the profiler to find out, then you will know which specific lines to tackle. Note: the profiler itself will slow things down, so remember to turn it off when you are finished profiling.


Optimising code:

  • You can get rid of needless operations like + ran2 - ran2, won't make a big difference but cleans things up.

    % old
    in = [(k0^2 * (n.^2 - nbar^2) - 2 * 1/stepx^2), (ran2 + 1/stepx^2 - ran2), (ran2 + 1/stepx^2 - ran2)];
    % new
    in = [(k0^2 * (n.^2 - nbar^2) - 2 * 1/stepx^2), 1/stepx^2, 1/stepx^2];
    
  • There are several things which don't change in your loop (like stepx), which should have their calculations taken outside the loop.

    stepx2 = 1/stepx^2;
    for j = 1:2500
      ...
      in = [(k0^2 * (n.^2 - nbar^2) - 2 * stepx2), stepx2, stepx2];
      ...
    

    This thinking can also be applied to things like length(H), which never changes but is calculated every loop iteration.

  • Getting rid of the waitbar stuff will speed things up too. A note about the waitbar calculation too, you shouldn't be using max as a variable name, it is a very common in-built function name which you've over-ridden!

  • Replace the m for loop with logical indexing

    ...
    xe2 = lp(j);
    
    n = zeros(nx, 1);      % Where nx was defined outside the loop as length(x)
    n(x < xs & x >= start) = 1;
    n(x > xe & x <= ende) = 1;
    n(x > xs2 & x < xe2) = 1;
    
    in = ...
    

    Or in one indexing line:

    n = zeros(nx, 1); % or n = zeros(size(x));
    n((x < xs & x >= start) | (x > xe & x <= ende) | (x > xs2 & x < xe2)) = 1;
    

Beyond that, you will have to look at the profiler results and see which lines to target.

\$\endgroup\$
5
  • \$\begingroup\$ Thanks for your answer. As I said in the comments above I tried the profiler but it takes so long that I thought maybe there is an obvious time consuming action that I dont know about.and about your tips. in the first one, one ran2 is in Nominator and the next one in denominator. you cant just eliminate them. and length(H) is changing in every iteration. The rest was useful. Thanks. \$\endgroup\$
    – dieKoderin
    Apr 25, 2017 at 12:22
  • \$\begingroup\$ @Mary, The way you have them set up, they are not in the numerator and denominator!! You need to use brackets, otherwise you can eliminate them (and Matlab will)! Use like ran2frac = (ran2 + 1)/(stepx^2 - ran2) before the loop, and then use that variable where appropriate. Please mark this answer as accepted if it helped. \$\endgroup\$
    – Wolfie
    Apr 25, 2017 at 12:43
  • \$\begingroup\$ I'm Sry. the first one was in order to have a vector not scalar value. and for the logical indexing, the way you wrote is not working. Because n and x are two different vectors. I am checking my x and changing my n. \$\endgroup\$
    – dieKoderin
    Apr 25, 2017 at 15:27
  • \$\begingroup\$ @Mary You can use logical indexing for "checking x and changing n", as long as they are the same size (which they are because I initialisen to be the same size as x). Note I made an error originally by using n = zeros(nx) instead of n = zeros(nx,1), or just n = zeros(size(x)), I've edited my answer. \$\endgroup\$
    – Wolfie
    Apr 25, 2017 at 15:31
  • \$\begingroup\$ The logical indexing should speed this up quite a bit. I'd guess that it's the bottleneck. \$\endgroup\$ Aug 29, 2017 at 6:12
1
\$\begingroup\$

MATLAB matrices are stored column-wise, not row-wise. That means, it's a lot faster to work on complete rows instead of complete columns.

I suggest changing the following line:

Intn((j)+1, :) = H .* conj(H);

to

Intn(:, j+1) = (H .* conj(H)).'   % or transposing it at some other point in the code.

Notice that I use .' and not '. That's because you're working with complex numbers, and ' is the complex conjugate transpose, not the regular transpose.


The following is taken from my SO answer here

%% Columns
a = rand(n);
b = zeros(n,1);
tic
for ii = 1:n
  b = b + a(:,ii);
end
toc
Elapsed time is 0.252358 seconds.

%% Rows:
a = rand(n);
b = zeros(1,n);
tic
for ii = 1:n
  b = b + a(ii,:);
end
toc
Elapsed time is 2.593381 seconds.

More than 10 times as fast when working on columns!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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