Search code examples
c++matlabeigen

Efficient Method to pass large arrays from Matlab to C++ Eigen using Map class


I am trying to pass large, complex matrices from Matlab to a C++ Mex API function that uses the Eigen library. I've come up with two approaches using Eigen's Map class to map data from an input TypedArray Complex double matrix to an Eigen complex double matrix. I believe that using the map class shouldn't yield any copy operations and therefore the execution time should be trivial, regardless of the size of the matrix.

Implementation 1:

matlab::data::TypedArray<std::complex<double>> inIDXT = std::move(inputs[1]);
Eigen::MatrixXcd idxt = Eigen::Map< Eigen::MatrixXcd > (inIDXT.release().get(), numEl*na, nPoints);

Implementation 2:

matlab::data::TypedArray<std::complex<double>> inIDXT = std::move(inputs[1]);
auto idxtData = inIDXT.release();
Eigen::Map< Eigen::MatrixXcd > idxt(idxtData.get(), numEl*na, nPoints);

I measured the execution time using std::chrono::steady_clock::now() and I compiled and ran this in matlab using the mex command with MinGW64 Compiler which I believe is equivalent to gcc 6.3 (Unfortunately, more recent versions are not supported).

I found that for an input matrix of size 14400x73728 complex double, Implementation 2 takes ~30~40 seconds whereas Implementation 1 takes ~150 seconds. I know this is not the best way to profile code, but still, I am surprised that either approach takes any amount of time at all, let alone has a major difference between the two implementations.

What is going on here? If there is genuinely no copy operation going on, why does this operation consume time? Am I missing some subtlety? Or am I being naive here and matrices this large will take at least some time to construct a map object for? Also, why is there a difference in the runtime between the two versions? As far as I can tell, they should be the same.


Solution

  • There are some issues and misconceptions here:

    Note the documentation of release:

    Release the underlying buffer from the Array. If the Array is shared, a copy of the buffer is made; otherwise, no copy is made. After the buffer is released, the array contains no elements

    And its return value is buffer_ptr_t<T> which is a unique_ptr. Three consequences of that:

    1. The cost of the second can be from the copy if it was shared
    2. The first code is a bit dangerous because you don't retain the unique_ptr. However, your Map gets destroyed at the end of the statement so I think that use is okay
    3. In the second code you retain the unique_ptr but be careful: It has to live as long or longer than the Map, otherwise the Map will reference deleted data (a dangling pointer)

    The second point is that Eigen::MatrixXcd idxt = Eigen::Map creates a copy. An Eigen::Matrix owns its data. This initializes a matrix from a map type, meaning a copy. That explains the extra cost.