Provide a way to free up unused memory in a dynamic/sparse matrix

Issue #98 resolved
Benjamin Beach created an issue

This is important for two aspects of my application. For both, keep in mind that I am working on an application for which the matrices/vectors can become huge, and saving on memory is mission-critical.

1) I am trying to make my own constructor for the MatS file that mimic's Matlab's sparse constructor, which is incredibly useful for finite element computations. (The blaze version of this would something look like:

//initialize data i_indices, j_indices, values, m, n
MatS<type, layout> mat(i_indices, j_indices, values, m, n);

These i_indices and j_indices vectors are completely unsorted and usually contain duplicate entries for my application (i.e. pairs (i_indices(k),j_indices(k)) can be identical for multiple values of k). Seeing as Blaze seems to have no vector sorting functionality, and no such Matlab-style constructor, it is not possible to initialize this data in an efficient way (as described in the wiki) without the use of a manual sort function on the data to do a multi-priority sort (first by row index, then by column index) directly on the index vectors.

In particular, it it difficult (and possibly expensive) to pre-determine the required internal capacity of the matrix. Thus, this capacity is over-allocated initially as the size of the input vectors, but after the construction is complete, it is impossible to directly re-allocate this internal capacity (without doing silly, super-inefficient things like manually building a separate sparse matrix, then copying the data to the original matrix). An automatic tightening function would be quite useful here.

2) I need a way to free up the memory for matrices once I'm done with them. This one is possible to implement independently, but it requires the use of the new operator and, consequently, de-referencing each variable every time I want to perform a matrix-matrix operation on it, which is a very messy and undesirable workaround. It would be best to be able to free up the unused matrix capacity after clearing the matrix, so that I could reduce its memory footprint to a negligible amount until its scope expires.

Comments (4)

  1. Klaus Iglberger

    Hi Benjamin!

    Thanks a lot for the detailed proposal. We understand that this is a very desirable feature. Also, this idea nicely complements issue #46. We will therefore see to it as soon as possible.

    Till then, perhaps the good old temporary-swap-idiom helps you to reduce the capacity of a matrix. The following shrinkToFit() function works for all kinds of matrices (including DynamicMatrix and CompressedMatrix):

    template< typename MT, bool SO >
    void shrinkToFit( Matrix<MT,SO>& mat )
    {
       MT( ~mat ).swap( ~mat );
    }
    
    int main()
    {
       DynamicMatrix<int,rowMajor> A( 5UL, 5UL, 5 );
       A.reserve( 1000000 );
    
       std::cout << " A =\n" << A << "\n";
       std::cout << " Capacity of A = " << A.capacity() << "\n\n\n";
    
       shrinkToFit( A );
    
       std::cout << " A =\n" << A << "\n";
       std::cout << " Capacity of A = " << A.capacity() << "\n";
    }
    

    Output:

     A =
    (            5            5            5            5            5 )
    (            5            5            5            5            5 )
    (            5            5            5            5            5 )
    (            5            5            5            5            5 )
    (            5            5            5            5            5 )
    
     Capacity of A = 1000000
    
    
     A =
    (            5            5            5            5            5 )
    (            5            5            5            5            5 )
    (            5            5            5            5            5 )
    (            5            5            5            5            5 )
    (            5            5            5            5            5 )
    
     Capacity of A = 40
    

    Best regards,

    Klaus!

  2. Benjamin Beach reporter

    Hello Klaus,

    Thank you for the help; the swap workaround worked (kind of; the function as given gave errors, but templating with the matrix types directly worked).

    Looking forwards to the feature!

    Best, Ben Beach

  3. Klaus Iglberger

    Summary

    The feature has been implemented, tested, optimized and documented as required. It is immediately available via cloning the Blaze repository and will be officially released in Blaze 3.2.

    shrinkToFit()

    The internal capacity of vectors and matrices with dynamic memory is preserved in order to minimize the number of reallocations. For that reason, the resize() and reserve() functions can lead to memory overhead. The shrinkToFit() member function can be used to minimize the internal capacity:

    blaze::DynamicVector<int> v1( 1000UL );  // Create a vector of 1000 integers
    v1.resize( 10UL );                       // Resize to 10, but the capacity is preserved
    v1.shrinkToFit();                        // Remove the unused capacity
    
    blaze::DynamicMatrix<int> M1( 100UL, 100UL );  // Create a 100x100 integer matrix
    M1.resize( 10UL, 10UL );                       // Resize to 10x10, but the capacity is preserved
    M1.shrinkToFit();                              // Remove the unused capacity
    

    Please note that due to padding the capacity might not be reduced exactly to size(). Please also note that in case a reallocation occurs, all iterators (including end() iterators), all pointers and references to elements of the vector are invalidated.

  4. Log in to comment