0
$\begingroup$

I've converted a MATLAB code to C++ to speed it up, using the Armadillo library to handle matrix operations in C++, but surprisingly it is 10 times slower than the MATLAB code!

So I test the Armadillo library to see if it's the cause. The below code is a simple test code that initializes two matrices, adds them together and saves the result to a new matrix. One section of code uses the Armadillo library and other one doesn't. The section using Armadillo is too slow (notice the elapsed times).

Does it really slow down the execution (though it is supposed to speed it up) or am I missing some thing?

      #include<iostream>
      #include<math.h>
      #include<chrono>
      #include<armadillo>
      #include<time.h>
      #include<conio.h>
     using namespace std;
     using namespace arma;
     int main()
       {   
        srand((unsigned int)time(NULL));
        auto start = std::chrono::high_resolution_clock::now();
        double a[100][100];
        double b[100][100];
        double c[100][100];
        for (int i = 0; i < 100; i++)
           {
             for (int j = 0; j < 100; j++)
                {   
                 a[i][j] = rand() % 10;
                 b[i][j] = rand() % 10;
                 c[i][j] = a[i][j] + b[i][j];
                 }
            }

        auto finish = std::chrono::high_resolution_clock::now();
        std::chrono::duration<double> elapsed = finish - start;
        std::cout << "Elapsed time: " << elapsed.count() << " s\n";
        auto start1 = std::chrono::high_resolution_clock::now();
        mat a1= randi<mat>(100, 100, distr_param(1, 10));
        mat b1= randi<mat>(100, 100, distr_param(1, 10));
        mat c1=zeros(100,100);
        c1 = a1 + b1;
        auto finish1 = std::chrono::high_resolution_clock::now();
        std::chrono::duration<double> elapsed1 = finish1 - start1;
        std::cout << "Elapsed time: " << elapsed1.count() << " s\n";
        return 0;


    }

        }

Here is the answer I get:

Elapsed time: 0.00217959 s
Elapsed time: 0.00946147 s

As you see, Armadillo is significantly slower! Is it better not to use the Armadillo library?

$\endgroup$
14
  • 1
    $\begingroup$ It's possible that the compiler simply optimized out a and b and simply assigned all elements of c to 2. Armadillo has sufficiently many layers that it probably couldn't do that for c1. Could you examine the assembly? $\endgroup$
    – user14717
    May 17, 2018 at 11:22
  • $\begingroup$ @user14717 Unfortunately I'm not that familiar with assembly,but I will try to examine it. $\endgroup$
    – MAh2014
    May 17, 2018 at 11:35
  • 2
    $\begingroup$ These kinds of microbenchmarks can sometimes be tricky to get right. Could you please post the entire process you used, including compiler versions, compilation flags, optimization flags, that sort of thing? Simply knowing elapsed time is not enough to go on to give meaningful advice. Incidentally, one way to examine the produced assembly is with godbolt (godbolt.org/g/Mw4S3C), but unfortunately it wouldn't help with code that uses Armadillo. You can see in godbolt.org/g/Mw4S3C how clang discards a and b. $\endgroup$
    – Kirill
    May 17, 2018 at 12:03
  • $\begingroup$ @Kirill Environment :Intel(R) Core(TM) i5-4200M CPU ,2 Core(s), 4 Logical Processor(s) Visual studio 2017 version 15.4 armadillo 8.5 $\endgroup$
    – MAh2014
    May 17, 2018 at 12:15
  • 3
    $\begingroup$ @MAh2014 That's it, it looks like you're compiling in a debug mode, meaning your timing results are going to be completely meaningless. In general, it will do nothing to speed up sufficiently simple operations especially those that Matlab can handle itself (ones(100,100)+ones(100,100) is something Matlab is quite good at). Complicated stuff—sure, but even then it might not be worth the effort. I want to say that your questions are pretty broad, it might help you get a good answer if you narrow things down a bit (see, for example, meta.stackexchange.com/questions/66377). $\endgroup$
    – Kirill
    May 17, 2018 at 17:27

2 Answers 2

4
$\begingroup$

You're comparing statically-defined arrays which live on the stack:

    double a[100][100];
    double b[100][100];
    double c[100][100];

to heap allocated arrays:

    mat a1= randi<mat>(100, 100, distr_param(1, 10));
    mat b1= randi<mat>(100, 100, distr_param(1, 10));
    mat c1=zeros(100,100);

Of course, stack-allocated variables are more constrained but have faster access than heap allocated variables. Additionally, this loop:

             a[i][j] = rand() % 10;
             b[i][j] = rand() % 10;
             c[i][j] = a[i][j] + b[i][j];

doesn't actually need a and b if those are not returned from the function, so as @Kirill mentions in the comments some compilers will just completely remote those arrays and put it all into c.

(also make sure you're compiling correctly. Use higher optimization level flags like -O3, make sure it's not in debug mode, etc.)

So there's a few morals to this story. First of all, using stack-allocated arrays is awesome. You should do it whenever possible. But secondly, you shouldn't expect MATLAB vs Armadillo to be very different if all of the time is spent in MATLAB operations since MATLAB is essentially a library for calling things like Armadillo. Writing out the MATLAB code as a for loop will be slow, but its vectorized operations are very optimized and many times implicitly multithreaded. While you don't give MATLAB code here to actually show what's going on, that implicit multithreading could be the distinguishing factor in the timings. Of course, you can always multithread and parallelize your own code in C and get it just as good, but the advantage of vectorized programming languages like MATLAB is that the hard work on simple functions has already been done for you.

So what this is really saying is that MATLAB vectorization is fine/good when dealing with operations on the heap, or if you want to write out more complicated loops then you should try to use static arrays whenever possible and only go to heap-allocated array operations when necessary. Unfortunately, when working in something like MATLAB/Python/R your operations are with heap-allocated variables which is why they focus so much on vectorization and large array operations. Using C or Fortran gives you the choice between heap vs stack that these languages don't have, while libraries like Armadillo add on the MATLAB-like functionality. From the other direction, something like Julia does have libraries like StaticArrays.jl to utilize static arrays (and parallelism), so that's one option to both have the flexibility to mix the designs while staying in a higher level language. But no matter where you look, optimized vectorized heap operations will likely end up the same speed as MATLAB: it's the more complicated operations and loops which you would have to write yourself that make the speed difference.

$\endgroup$
3
  • $\begingroup$ Thank you ,although I had gotten my answer from @krill great advices, your answer was instructive too. $\endgroup$
    – MAh2014
    May 27, 2018 at 18:17
  • $\begingroup$ The "stack variables have faster access than heap variables" sounds wrong. In the link it says "Heap memory is slightly slower to be read from and written to, because one has to use pointers to access memory on the heap.", which is false, pointers don't work that way. Indirect access is slower, which is why pointers are sometimes avoided, but that's nothing to do with stack/heap. As an example to check for yourself, see how the compiler accesses arrays on the stack and the heap in godbolt.org/g/2D1CQz: both are just loads of pointer+offset. $\endgroup$
    – Kirill
    May 31, 2018 at 11:13
  • $\begingroup$ I think they were trying to make the point that int f(int a, int b) { return a+b; } is better than int f(int *a, int *b) { return *a+*b; }, and said it rather imprecisely. But in any case that wouldn't matter at all here, because every array is accessed through a pointer and all the pointers are the same. $\endgroup$
    – Kirill
    May 31, 2018 at 11:17
0
$\begingroup$

Have you tried initialising c1 to memory?

c1=zeros(100,100);

I think it doesn't allocate the memory to the variable until you fill it.

The result when I run your code:

Elapsed time: 0.0002079 s Elapsed time: 0.0003315 s

Difference not as bad... I'm no expert but maybe the way your armadillo is installed?

$\endgroup$
10
  • $\begingroup$ ,I tried it now,I still get the same result. $\endgroup$
    – MAh2014
    May 17, 2018 at 11:41
  • $\begingroup$ Interesting. I'm also using armadillo because it's meant to be "faster". Would upvote your question but I got 1 rep lol $\endgroup$
    – Brani
    May 17, 2018 at 11:45
  • $\begingroup$ Does the problem scale with the matrix size (say 1000 x 1000)? $\endgroup$
    – Brani
    May 17, 2018 at 11:53
  • $\begingroup$ It crashes when I changes to 1000(I think it's because lack of memory),but the problem exists for say 500. $\endgroup$
    – MAh2014
    May 17, 2018 at 12:00
  • 2
    $\begingroup$ @MAh2014 That's because you're allocating the arrays on the stack rather than the heap, which is usually wrong anyway. It's not lack of memory, it's the default stack size limit. $\endgroup$
    – Kirill
    May 17, 2018 at 12:08

Your Answer

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

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