3

I am trying to use a custom new-handler to respond to out of memory errors in a program where I use the Eigen library.

When I solve an instance, I cache the results (a large dynamic size vector), so if I need to solve that instance again I already have the results. After a while it may happen that too many instances are cached, so there is not enough memory left during computation. In this case I would empty the cache (or better remove some instances).

The simplest way I came up with is to have a global variable std::map<InputType, ResultType> cache, and to install a new-handler that empties it (unless it is already empty, then it falls back on the default handler). In this way when an allocation fails my handler frees some memory and returns, so another attempt is done to allocate memory.

However it seems that such handler is never called, instead a std::bad_alloc exception is always thrown.

I tried this example taken from here. When using std::vector<int> the custom handler is correctly called, and "Memory allocation failed, terminating" is printed. When using Eigen::VectorXi I only got a std::bad_alloc exception.

#include <Eigen/Dense>

#include <iostream>
#include <vector>
#include <new>

void handler()
{
    std::cout << "Memory allocation failed, terminating\n";
    std::set_new_handler(nullptr);
}

int main()
{
    std::set_new_handler(handler);
    try {
        while(true) {
            const unsigned long int Size = 100000000ul;

            // Either one of the following:

            // new Eigen::VectorXi(Size);
            new std::vector<int>(Size);
        }
    } catch (const std::bad_alloc & e) {
        std::cout << e.what() << '\n';
    }
}

I wonder why this happens, perhaps Eigen does not use operator new to allocate memory? I could not find anything in the documentation. Is there a way to use this technique with Eigen? Or must I catch the exception at high level, clear the cache, and perform the computation again?

Claudio
  • 1,658
  • 11
  • 18

2 Answers2

2

Check out this file in Eigen source code : Eigen/src/Core/util/Memory.h , to see what Eigen is doing in this department. IIRC we do throw bad_alloc explicitly on allocation failure. Feel free to write to the mailing list if we should change something.

Benoit Jacob
  • 351
  • 2
  • 3
  • Thanks for the advice, I managed to achieve the behaviour I was looking for. I posted a new answer with the details. – Claudio Oct 22 '12 at 10:17
2

Thanks to Benoit Jacob's advice I tracked down the part where Eigen allocates memory. The function is aligned_malloc() defined in Eigen/src/Core/util/Memory.h. It turned out it does not use an operator new, but it calls malloc().

I found this example for implementing operator new and modified the function aligned_malloc() accordingly:

inline void* aligned_malloc(size_t size)
{
  check_that_malloc_is_allowed();

  while (true) {
    void *result;
    #if !EIGEN_ALIGN
      result = std::malloc(size);
    #elif EIGEN_MALLOC_ALREADY_ALIGNED
      result = std::malloc(size);
    #elif EIGEN_HAS_POSIX_MEMALIGN
      if(posix_memalign(&result, 16, size)) result = 0;
    #elif EIGEN_HAS_MM_MALLOC
      result = _mm_malloc(size, 16);
    #elif (defined _MSC_VER)
      result = _aligned_malloc(size, 16);
    #else
      result = handmade_aligned_malloc(size);
    #endif

    if(result || !size) {
      return result;
    }

    std::new_handler currentHandler = std::set_new_handler(0);
    std::set_new_handler(currentHandler);

    if (currentHandler) {
      currentHandler();
    } else {
      throw_std_bad_alloc();
    }
  }
}

Now the memory handler is correctly called when an allocation fails (there are other allocation functions that might need to be modified as well, but it seems they are all wrapped by aligned_malloc() so a check in that function might cover everything).

However at this point I am not sure whether Eigen should honour the new handler: it gets memory from malloc() which does not use exceptions, but it notifies malloc() failures by throwing std::bad_alloc.

Community
  • 1
  • 1
Claudio
  • 1,658
  • 11
  • 18