Timing

How to time? Get number of days (as a fraction too) since January 1st, 1970 with $\texttt{now()}$

In [20]:
format long % change default printing format to better visualize days
now() / 365.25
format % reset the default printing format to the default
ans =  2019.040075803456

Get a more useful time format with $\texttt{clock()}$

In [21]:
% return the current date in years, months, days, hours, minutes and seconds
clock()

% you can use that to time execution
t1 = clock();
A = pinv(rand(5e2, 5e2)); % invert a REALLY large matrix
t2 = clock()

elapsed = t2 - t1
el_sec = elapsed(end)
ans =

   2019.0000      1.0000     29.0000      9.0000     18.0000     51.0715

t2 =

   2019.0000      1.0000     29.0000      9.0000     18.0000     51.3742

elapsed =

   0.00000   0.00000   0.00000   0.00000   0.00000   0.29724

el_sec =  0.29724

Use dedicated tool for timing code $\texttt{tic()}$ and $\texttt{toc()}$

In [24]:
N = 10;
tic();
for i = 1:10
    A = pinv(rand(5e2, 5e2));
end
toc(); % by itself, toc() prints the time since the last tic()
Elapsed time is 2.8868 seconds.
In [4]:
% use a reference to the tic to inform toc which one to measure
t_total = tic();
for i = 1:5
    t_loop = tic();
    A = pinv(rand(5e2, 5e2));
    toc(t_loop);
end
toc(t_total);
Elapsed time is 0.277044 seconds.
Elapsed time is 0.311869 seconds.
Elapsed time is 0.294305 seconds.
Elapsed time is 0.300725 seconds.
Elapsed time is 0.281532 seconds.
Elapsed time is 1.4748 seconds.
In [26]:
% capture the output of toc to use as a number
N = 10;
t = tic();
for i = 1:N
    A = pinv(rand(5e2, 5e2));
end
elaps = toc(t); % nothing printed when we capture the value

fprintf('Time elapsed was %e min, wait, seconds\n', elaps / N)
elaps / N
Time elapsed was 2.819258e-01 min, wait, seconds
ans =  0.28193

Preallocation

In [30]:
a = [1, 2, 3];
a(end + 5) = 2.0
a(end + 1) = 1.0
a =

   1   2   3   0   0   0   0   2

a =

   1   2   3   0   0   0   0   2   1

In [6]:
% you can resize arrays in Matlab
a = [1, 2, 3]

% add numbers to just past the end
a(4) = 2; 
a(end + 1) = 3;
a % print a

% you can add numbers way past the end
% missing values will be 0
a(30) = 17;
a % print a

% this works too
a = [1, 2, 3];
a(end + 4) = 17;
a

% a as a container for while loops
a = []; % a is empty
c = 0.5;
i = 1;
while c^i > 1e-5
    a(i) = c^i;
    i = i + 1;
end
a
a =

   1   2   3

a =

   1   2   3   2   3

a =

 Columns 1 through 16:

    1    2    3    2    3    0    0    0    0    0    0    0    0    0    0    0

 Columns 17 through 30:

    0    0    0    0    0    0    0    0    0    0    0    0    0   17

a =

    1    2    3    0    0    0   17

a =

 Columns 1 through 5:

   0.500000000   0.250000000   0.125000000   0.062500000   0.031250000

 Columns 6 through 10:

   0.015625000   0.007812500   0.003906250   0.001953125   0.000976562

 Columns 11 through 15:

   0.000488281   0.000244141   0.000122070   0.000061035   0.000030518

 Column 16:

   0.000015259

Try to preallocate arrays, it makes them faster

In [31]:
N = 1e5;

t_prealloc = tic();
a = ones(1, N);
i = 2;
while i <= N
    a(i) = 1.25 * a(i - 1);
    i = i + 1;
end
el_prealloc = toc(t_prealloc)

t_nopre = tic();
b = [1];
i = 2;
while i <= N
    b(i) = 1.25 * b(i - 1);
    i = i + 1;
end
el_nopre = toc(t_nopre)

el_nopre / el_prealloc

% Octave's not really optimized, it gives a more pronounced effect in Matlab
el_prealloc =  0.91048
el_nopre =  0.97675
ans =  1.0728

Vectorization

Avoiding for-loops by all means possible.

In [32]:
% let's sum a hundred thousand random numbers
N = 1e5;

t = tic();
a = 0;
for i = 1:1e5
    a = a + rand();
end
toc(t);

t = tic();
a = sum(rand(1, 1e5));
toc(t);
Elapsed time is 0.716366 seconds.
Elapsed time is 0.00389194 seconds.
In [33]:
T_loop = zeros(1, length(0:6));
j = 1;
for N = 10.^(0:6);
    t = tic();
    
    % start the computation
    a = 0;
    for i = 1:N
        a = a + rand();
    end
    % end the computation
    
    el_loop = toc(t);
    
    T_loop(j) = el_loop;
    j = j + 1;
end

T_vect = zeros(1, length(0:(length(T_loop) - 1)));
j = 1;
for N = 10.^(0:6);
    t = tic();
    
    % start the computation
    a = sum(rand(1, N));
    % end the computation
    
    el_vect = toc(t);
    
    T_vect(j) = el_vect;
    j = j + 1;
end

figure();
plot(0:(length(T_loop) - 1), T_loop ./ T_vect, 'LineWidth', 7);
title('Speed-up when using vectorization', 'FontSize', 18);
xlabel('Number of random samples to sum', 'FontSize', 18)
ylabel('Speed-up (times)', 'FontSize', 18);
Gnuplot Produced by GNUPLOT 5.2 patchlevel 6 0 200 400 600 800 1000 0 1 2 3 4 5 6 Speed-up (times) Number of random samples to sum Speed-up when using vectorization gnuplot_plot_1a

Use element-wise operations

In [10]:
a = 2.^(1:10)
x = 1:10;
y = 10:-1:1;
x .* y

a = [2, 3, 5, 7, 9] ./ (5:-1:1)
a =

      2      4      8     16     32     64    128    256    512   1024

ans =

   10   18   24   28   30   30   28   24   18   10

a =

   0.40000   0.75000   1.66667   3.50000   9.00000

Use vector indexing

In [36]:
a = [2, 3, 5]
a([3, 1, 1])
a =

   2   3   5

ans =

   5   2   2

In [40]:
a = linspace(0, 20, 21);
% select every other a
a(1:2:end)

% reverse a
a(end:-2:1)
ans =

    0    2    4    6    8   10   12   14   16   18   20

ans =

   20   18   16   14   12   10    8    6    4    2    0

In [12]:
b = ones(1, 10);
b(1:2:end) = b(1:2:end) * -1
b =

  -1   1  -1   1  -1   1  -1   1  -1   1

In [42]:
a = [1, 2, 3, 4, 5];
b = [-1, -2, -3, -4, -5];
c = zeros(1, length(a) + length(b));

c(1:2:end) = a;
c(2:2:end) = b;

c([2, 2])

c
ans =

  -1  -1

c =

   1  -1   2  -2   3  -3   4  -4   5  -5

Use logical indexing

In [47]:
format compact

T = zeros(3, 10);

a = randi(10, 1, 10)
T(1, :) = a;

% logical indices, same size as array, number 1 for true and 0 for false
idxs = (a > 5) & (a < 9)
T(2, :) = (a > 5) & (a < 9);

a(idxs) = Inf
T(3, :) = a;

T
a =
    8    3    2    6    2    2    9   10    4    4

idxs =
  1  0  0  1  0  0  0  0  0  0

a =
   Inf     3     2   Inf     2     2     9    10     4     4

T =
     8     3     2     6     2     2     9    10     4     4
     1     0     0     1     0     0     0     0     0     0
   Inf     3     2   Inf     2     2     9    10     4     4

Extensive example: particles moving randomly on a plane

In [15]:
N = 1e3;
moves_nb = 50;

x = zeros(1, N);
y = zeros(1, N);


t = tic();
for i = 1:moves_nb
    for j = 1:N
        move = randi(4);
        if move == 1 % right
            x(j) = x(j) + 1;
        elseif move == 2 % down
            y(j) = y(j) - 1;
        elseif move == 3 % left
            x(j) = x(j) - 1;
        elseif move == 4 % up
            y(j) = y(j) + 1;
        end
    end
end
toc(t);

figure();
plot(x, y, '.', 'MarkerSize', 20);
Elapsed time is 7.53978 seconds.
Gnuplot Produced by GNUPLOT 5.2 patchlevel 6 -20 -15 -10 -5 0 5 10 15 -15 -10 -5 0 5 10 15 20 gnuplot_plot_1a
In [16]:
N = 1e3;
moves_nb = 50;

x = zeros(1, N);
y = zeros(1, N);


t = tic();
for i = 1:moves_nb
    r = randi(4, 1, N);
    x(r == 1) = x(r == 1) + 1; % right
    y(r == 2) = y(r == 2) - 1; % down
    x(r == 3) = x(r == 3) - 1; % left
    y(r == 4) = y(r == 4) + 1; % up
end
toc(t);

figure();
plot(x, y, '.', 'MarkerSize', 20);
Elapsed time is 0.028753 seconds.
Gnuplot Produced by GNUPLOT 5.2 patchlevel 6 -15 -10 -5 0 5 10 15 20 -15 -10 -5 0 5 10 15 20 gnuplot_plot_1a

Make use of reducing functions

In [17]:
a = rand(1, 1e5);
mean(a)
sum(a)
std(a)
median(a)
[val, idx] = max(a)
[val, idx] = min(a)
ans =  0.50126
ans =  50126.00759
ans =  0.28901
ans =  0.50234
val =  0.99999
idx =  13767
val =  0.0000056840
idx =  77519

Use in-built functions

In [48]:
N = 1e7;
a = 100.0 * 2.0 * (rand(1, N) - 0.5); % numbers between -100 and 100

function y = my_abs(x)
    y = x;
    y(y < 0.0) = -y(y < 0.0);
end

% time our function (vectorized no less)
t_own = tic();
b = my_abs(a);
el_own = toc(t_own)

% time the in-built one
t_inb = tic();
b = abs(a);
el_inb = toc(t_inb)

el_own / el_inb
el_own =  0.13916
el_inb =  0.051063
ans =  2.7253
In [ ]: