Vectorization in Matlab Not Always Necessary

What separates Matlab from many other programming languages is the ability to vectorize code.  Vectorization allows a programmer to write code that is more intuitive, more concise, and often faster than using standard for, while and if statements.  If you have to do a large data processing task or need to create real-time application that does a large number of mathematical operations, vectorization is often a good option.  However, vectorization is not always the faster alternative for time-sensitive tasks.   I created tests for six tasks and compared the amount of time needed to run the vectorized and non-vectorized code.  The results were mixed, showing a decrease in running time for vectorized code on some tasks and a increase in running time on other tasks.  Tests involving mathematical operations ran faster using vectorized code, while tests involving conditional operators and vector creation ran faster for non-vectorized code.

[table id=13 /]

For each task, I created a non-vectorized and a vectorized version of code.  I always preallocated arrays for the non-vectorized code, to make the comparisons fair.  Preallocation decreases running time by a tremendous amount and is necessary for any tasks that are remotely time-sensitive .  I also used Monte Carlo simulations with 1000 iterations to obtain more precise values for the ratios between the two versions of the code.  For tests that used random numbers, I set the random number generator to the same state so that the two versions of code use the same random numbers.

I created six tests that represent common tasks in Matlab.  The first test was named the “Polynomial Test” and involved adding the first through fourth powers of a list of random numbers.  The second test was named the “Logarithm Test” and computed the natural logarithm of a 2-D array of random numbers.  The “Exponent Test” takes the number 3 to the power of an array of random numbers and saves the results in a new array.  The “Multiplication Test” computes the product of two numbers, each selected from a different array of random numbers.  The “Vector Creation Test” creates a horizontal vector using two different methods.  The last test is the “Conditional Test”, which finds indices where values of two arrays of random numbers are greater than a threshold.  If statements are used in the non-vectorized version of the last test, while the find function is used in the vectorized version.

The results from the tests are in the table above, in order of the tests that showed the greatest to smallest decrease in run time using vectorization.  The second column shows the results of the tests, which are expressed as the ratio between the time needed to run the vectorized code and the time needed to run the non-vectorized code.  A number below 1.0 indicates that vectorization was faster, while a number above 1.0 indicates the opposite.  The tests that used mathematical operations on a single vector decreased their run time the most using vectorization.  Multiplication of two different arrays also performed slightly faster when vectorized.  However, vector creation was slower using vectorization, which seems strange considering that this task is so often vectorized.  The Conditional Test was by far the slowest task when vectorized.  This is also surprising, since examples with conditional statements are often utilized to show the advantages of vectorization, but when preallocation and trimming are used, non-vectorized code using for and if statements actually runs faster.

These results were quite surprising to me, as they go against my previous beliefs about vectorization and my general coding practices.  In general, mathematical operations should be vectorized, although there is sometimes a limited benefit from doing so for some operations, such as exponents and multiplication of two arrays.  However, vector creation is performed slightly faster by non-vectorized code, and non-vectorized code performs conditional statements much more quickly.  When writing time-sensitive applications, programmers should take these results into account, as a 5% decrease in run time can result in a savings of hours for large data processing jobs.  However, for applications that are not time-sensitive, programmers should consider readability and ease of programming, as well.  Some code is quite difficult to write in a vectorized form, and there may be only a limited benefit in run time.  Additionally, some code is more difficult and time-consuming to write in non-vectorized form, especially for experienced Matlab programmers.  For instance, vector creation is 5% slower when vectorized, but is more readable and concise than the non-vectorized version.  Conditional tasks are significantly slower when vectorized, but for tasks that will not take long to process, the vectorized code may be the better choice in terms of readability and ease of coding.  What these tests show is that vectorization is not always desirable, and programmers should not worry too much about whether to vectorize or not vectorize code for tasks that are not time-sensitive.  In the end, the programmer should consider the trade-offs for each project and write code that is most suitable for his or her current task.

Vectorization Comparison Tests

Polynomial Test

N = 1000;
nv = zeros(N,1);
v = zeros(N,1);
ratio = zeros(N,1);

rand('state', 0)

for n = 1:N
    x = rand(100000,1);
    tic
    e = zeros(1,length(x));
    for k = 1:length(x)
         e(k) = x(k)^4 + x(k)^3 + x(k)^2 + x(k);
    end
    nv(n) = toc;
    clear e
end

rand('state', 0)

for n = 1:N
    x = rand(100000,1);
    tic
    s = x.^4 + x.^3 + x.^2 + x;
    v(n) = toc;
    clear s
end

ratio = v./nv;

disp('Poly Test')
disp(['Mean Ratio = ' num2str(mean(ratio))])

Logarithm Test

N = 1000;
nv = zeros(N,1);
v = zeros(N,1);
ratio = zeros(N,1);

rand('state', 0)

for n = 1:N
    x = rand(500,500);
    tic
    e = zeros(size(x));
    for k = 1:size(x,1)
        for j = 1:size(x,2)
            e(k,j) = log(x(k,j));
        end
    end
    nv(n) = toc;
    clear e
end

rand('state', 0)

for n = 1:N
    x = rand(500,500);
    tic
    s = log(x);
    v(n) = toc;
    clear s
end

ratio = v./nv;

disp('Log Test')
disp(['Mean Ratio = ' num2str(mean(ratio))])

Exponent Test

N = 1000;
nv = zeros(N,1);
v = zeros(N,1);
ratio = zeros(N,1);

rand('state', 0)

for n = 1:N
    x = rand(100000,1);
    tic
    e = zeros(1,length(x));
    for k = 1:length(x)
         e(k) = 3^x(k);
    end
    nv(n) = toc;
    clear e
end

rand('state', 0)

for n = 1:N
    x = rand(100000,1);
    tic
    s = 3.^x;
    v(n) = toc;
    clear e
end

ratio = v./nv;

disp('Expo Test')
disp(['Mean Ratio = ' num2str(mean(ratio))])

Multiplication Test

N = 1000;
nv = zeros(N,1);
v = zeros(N,1);
ratio = zeros(N,1);

rand('state', 0)

for n = 1:N
    x = rand(1000000,1);
    y = rand(1000000,1);

    tic
    e = zeros(1,length(x));
    for k = 1:length(x)
         e(k) = x(k)*y(k);
    end
    nv(n) = toc;
    clear e
end

rand('state', 0)

for n = 1:N
    x = rand(1000000,1);
    y = rand(1000000,1);

    tic
    s = x.*y;
    v(n) = toc;
    clear s
end

ratio = v./nv;

disp('Mult Test')
disp(['Mean Ratio = ' num2str(mean(ratio))])

Vector Creation Test

N = 1000;
nv = zeros(N,1);
v = zeros(N,1);
ratio = zeros(N,1);

for n = 1:N
    tic
    g = zeros(1000000,1);
    for m = 1:1000000
         g(m) = 3*m;
    end
    nv(n) = toc;
    clear g
end

for n = 1:N
    tic
    h = 3:3:3000000;
    v(n) = toc;
    clear h
end

ratio = v./nv;

disp('Create Vector Test')
disp(['Mean Ratio = ' num2str(mean(ratio))])

Conditional Test

N = 1000;
nv = zeros(N,1);
v = zeros(N,1);
ratio = zeros(N,1);

rand('state', 0)

for n = 1:N

    X = randn(1000000,1);
    Y = randn(1000000,1);

    tic
    A = zeros(size(X));
    c = 1;
    for k = 1:length(X)
        if (Y(k) > 0.3) & (X(k) > 0.3)
            A(c) = k;
            c = c + 1;
        end
    end
    A = A(1:c-1);
    nv(n) = toc;
    clear A
end

rand('state', 0)

for n = 1:N
    X = randn(1000000,1);
    Y = randn(1000000,1);

    tic
    B = find((Y > 0.3) & (X > 0.3));
    v(n) = toc;
    clear B
end

ratio = v./nv;

disp('If Test')
disp(['Mean Ratio = ' num2str(mean(ratio))])

4 thoughts on “Vectorization in Matlab Not Always Necessary

  1. This is an interesting question and post. Most specifically, it is significant if the vectorized codes are indeed slower. However, I find that I can not reproduce your results. On a mac mini with Matlab 2012a I get the ratio from the conditional test to be 0.884. Furthermore, you typically want to find a list of elements like you describe so that you can do something to them. I modified your code to find the same elements and set them to zero. It turns out that this is even faster if you use logical indexing (See below). So in the end, it is not clear to me that vectorization is ever slower than non-vectorized code (at least in the current release).

    %% Code that shows faster method using logical indexing
    N = 1000;
    nv = zeros(N,1);
    v = zeros(N,1);
    vf=zeros(N,1);
    ratio = zeros(N,1);

    rand(‘state’, 0)

    for n = 1:N

    X = randn(1000000,1);
    Y = randn(1000000,1);

    tic
    %A = zeros(size(X));
    c = 1;
    for k = 1:length(X)
    % if (Y(k) > 0.3) & (X(k) > 0.3)
    if (Y(k) > 0.3) && (X(k) > 0.3)
    Y(k)=0;
    % A(c) = k;
    % c = c + 1;
    end
    end
    % A = A(1:c-1);
    nv(n) = toc;
    clear A
    end

    rand(‘state’, 0)

    for n = 1:N
    X = randn(1000000,1);
    Y = randn(1000000,1);

    tic
    B = find((Y > 0.3) & (X > 0.3));
    Y(B)=0;
    v(n) = toc;
    clear B
    end

    for n = 1:N
    X = randn(1000000,1);
    Y = randn(1000000,1);

    tic
    Y(Y>0.3&X> 0.3) = 0;
    vf(n) = toc;
    clear B
    end

    ratio = v./nv;

    disp(‘If Test’)
    disp([‘Mean Ratio v/nv = ‘ num2str(mean(ratio))])
    disp([‘Mean Ratio vf/nv= ‘ num2str(mean(vf./nv))])

    • Colin,

      Thank you very much for your comment and for taking the time to test my claims. I ran the Conditional Test from this article again and found a ratio of 1.52, which is quite close to the original value in the article. I ran your code and found ratios of v/nv = 5.66 and vf/nv = 5.04. Clearly, on my machine, the vectorized code is slower than the non-vectorized code.

      I am not sure about why we would produce such different results on our two systems. My version of Matlab (R14, from 2004) is 8 years older than your version. I am running Matlab on a Mac, but on virtual Windows 7 using Parallels. I’m not sure if my operating system would make a difference. I suspect that the Matlab version number is responsible for the differences in our results.

      However, I do think that your results are important in that you show that for at least one system using the most recent version of Matlab, the vectorization of conditional statements is a faster option. I do not have access to a wide variety of Matlab versions, so I cannot exhaustively test this code on every version and every operating system. My only advice for users who require high-speed Matlab applications is to use vectorization by default but be aware of the possibility that a non-vectorized approach could be faster.

      -Eric

  2. Interesting comparisons. I would like to know a couple of things in analyzing the results. What version of MATLAB are you using? Were the comparisons done inside a function or a script (Oftentimes a loop performs better in a function than a script with some versions of MATLAB)? What are the results for the polynomial experiment if more terms are included? For example:


    for k = 1:length(x)
    T = x(k);
    e(k) = T.^12 + T.^11 + T.^10 + T.^9 + T.^8 +...
    T.^7 + T.^6 + T.^5 + T.^4 + T.^3 + T.^2 + T;
    end

    • Hi Matt,

      I used Matlab Version 7.0.0.19920 (R14) to run the tests. I am running this version of Matlab on a Windows 7 virtual machine on a Macbook Pro 5.5 using Parallels 5 on OS X 10.6.7.

      The comparisons were done inside a script. The code that you see in the post was copied directly from the .m files without any modifications. I modified the “polynomial test” so that the parts that are enclosed by the tic and toc statements are called from functions for both the non-vectorized and vectorized code. The ratio of vectorized to non-vectorized code was 0.63504, which shows less of an advantage for vectorization than the original code. Using functions in the code made vectorization a less attractive option.

      I also modified the polynomial test so that it would calculate a series of exponents from 1 to 12, like your example. I got a value of 0.76153 for the ratio of vectorized to non-vectorized code for this version of the polynomial test, compared to a value of 0.54731 for the original polynomial test. For this modification of the polynomial test, the advantage gained from vectorizing the code was approximately half that of the original polynomial test.

      Thanks for the great questions,
      Eric

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.