3

Eigenライブラリを使用するプログラムでメモリ不足エラーに対応するために、カスタムの new-handler を使用しようとしています。

インスタンスを解決すると、結果 (大きな動的サイズのベクトル) がキャッシュされるため、そのインスタンスを再度解決する必要がある場合は、既に結果が得られています。しばらくすると、キャッシュされたインスタンスが多すぎて、計算中に十分なメモリが残らない場合があります。この場合、キャッシュを空にします (または、いくつかのインスタンスを削除したほうがよいでしょう)。

私が思いついた最も簡単な方法は、グローバル変数std::map<InputType, ResultType> cacheを用意し、それを空にする new-handler をインストールすることです (既に空でない限り、デフォルトのハンドラーにフォールバックします)。このように、割り当てが失敗した場合、ハンドラーはメモリの一部を解放して戻るため、メモリを割り当てるための別の試行が行われます。

ただし、そのようなハンドラーは呼び出されないようで、代わりにstd::bad_alloc常に例外がスローされます。

hereから取ったこの例を試しました。カスタム ハンドラーを使用std::vector<int>すると、正しく呼び出され、"メモリの割り当てに失敗しました。終了しています" が出力されます。使用するEigen::VectorXiと、std::bad_alloc例外が発生しました。

#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';
    }
}

なぜこれが起こるのだろうか、おそらくEigenはoperator newメモリの割り当てに使用していないのでしょうか? ドキュメントには何も見つかりませんでした。このテクニックを Eigen で使用する方法はありますか? それとも、高レベルで例外をキャッチし、キャッシュをクリアして、計算を再度実行する必要がありますか?

4

2 に答える 2

2

Eigen ソース コードのこのファイルをチェックしてください: Eigen/src/Core/util/Memory.h で、Eigen がこの部門で何をしているかを確認してください。IIRC では、割り当ての失敗時に明示的に bad_alloc をスローします。何か変更が必要な場合は、遠慮なくメーリング リストに書き込んでください。

于 2012-10-16T22:19:53.730 に答える
2

Benoit Jacob のアドバイスのおかげで、Eigen がメモリを割り当てる部分を突き止めました。関数は でaligned_malloc()定義されていEigen/src/Core/util/Memory.hます。を使用していないことが判明しましたoperator newが、 を呼び出しますmalloc()

私は実装するためのこのoperator newを見つけ、それに応じて関数を修正しましたaligned_malloc():

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();
    }
  }
}

割り当てが失敗したときにメモリ ハンドラが正しく呼び出されるようになりました (変更が必要な可能性のある他の割り当て関数もありますが、それらはすべてラップされているように見えるaligned_malloc()ため、その関数のチェックですべてがカバーされる可能性があります)。

ただし、この時点では、Eigen が新しいハンドラーを尊重する必要があるかどうかはわかりません。malloc()例外を使用しないメモリを取得しますが、例外malloc()をスローして失敗を通知しstd::bad_allocます。

于 2012-10-22T10:16:43.450 に答える