【问题标题】:Why might I get heap corruption using Armadillo matrices with pybind11?为什么使用 pybind11 使用犰狳矩阵会导致堆损坏?
【发布时间】:2021-11-12 11:43:56
【问题描述】:

我已经为此工作了几周,无法在我的代码库之外制作可重现的示例。这就是为什么我需要帮助!我不确定这是否是 pybind11 或犰狳的问题。 Carma 没有问题,因为它发生在没有进行转换的情况下。

编辑This actually does appear to be a bug in Carma and my MRE is working.

我一直试图将其归结为MRE and have been unsuccessful。我将在这里解释我所知道的,但由于这不足以重现错误,所以我最需要的是一些关于在哪里查找的想法。这基本上是一个非常难以重现的内存损坏错误(堆损坏,Windows fatal exception: code 0xc0000374)。当我有一个未初始化的矩阵 A 并为其分配一个足够大的矩阵以使犰狳获取内存时,似乎会发生这种情况。释放该内存时会发生崩溃。

我在 Windows 10 上,使用 Armadillo 10.6.2 和 pybind11 v2.7.1,使用 Clang 11 编译。

// mat_container.cpp
#include "mat_container.h"

MatrixContainer::MatrixContainer(size_t d) {
    A = arma::Mat<double>(d, d, arma::fill::eye);
    std::cerr << "filled arma matrix\n";
}

绑定代码

#include <pybind11/numpy.h>
#include <pybind11/pybind11.h>
#include <armadillo>
#include <carma>
#include "mat_container.h"

PYBIND11_MODULE(example, m) {
  py::class_<MatrixContainer>(m, "MC").def(py::init<size_t, bool>())
       .def_readwrite("A", &MatrixContainer::A);
}

触发崩溃我需要做的就是通过 Python 调用 example.MC(11) 并在释放成员变量的矩阵析构函数(不是分配给它的临时变量)中的内存时崩溃。犰狳在崩溃前调试消息:

@ __cdecl arma::Mat<double>::~Mat(void) [eT = double] [this = 000001569470DBA0]
Mat::destructor: releasing memory

在我的attempted MRE 中,我尝试使用在单独的库中编译的这个 MatrixContainer 类的绑定代码来重现相同的结构。我不知道可能缺少什么,所以我的 MRE 不会重现该错误。

奇怪的事情

  • 仅在从 pybind11 调用时发生,而不是纯 C++
  • 仅当包含矩阵的类的构造函数位于单独编译的源文件中时才会发生。当构造函数在头文件中时不会发生。
  • 仅在将非标识(例如 np.ones 但不是 np.eye)矩阵分配给对象的矩阵成员变量时才会发生。 但这只会发生在我的源代码中的一个类上,而不是我创建的较小的测试类上!
  • 仅当 A 大到足以让犰狳获取内存而不是使用本地内存时才会发生
  • 在分配 A 之前使用 set_size() 设置 A 的大小并不能解决问题
  • 这似乎是犰狳特有的,当犰狳释放内存时(使用ARMA_EXTRA_DEBUG 确认),因为当我使用另一个具有动态分配内存的类(如矢量)时,我没有收到类似的错误

当A使用获取的内存,被使用本地内存的矩阵替换时,会发生崩溃

mc = MC(11)
mc.A = np.eye(3)

在崩溃前的 Arma 调试消息,因为正在分配 mc.A:

@ void __cdecl arma::Mat<double>::init_warm(arma::uword, arma::uword) [eT = double] [in_n_rows = 3, in_n_cols = 3]
Mat::init(): releasing memory

但不是在相反的情况下;这运行成功。它不会因为 A 被销毁而释放获取的内存而崩溃:

mc = MC(3)
mc.A = np.eye(11)

当一个 11x11 A 被 numpy 中的另一个 11x11 矩阵替换时,崩溃再次是 Mat::destructor: releasing memory,就像第一个示例中没有来自 Python 的赋值一样。 我从这些示例中得出的结论是,当将由 Carma(不是 Armadillo 直接)准备的获取内存矩阵分配给本地内存 A 时,可以避免崩溃。

更新: 仍在处理此问题,但看起来代码失败了,因为 arma_extra_code 已打开(它不在 MRE 中)。罪魁祸首似乎是ARMA_ALIEN_MEM函数,设置here

【问题讨论】:

    标签: python c++ armadillo pybind11


    【解决方案1】:

    感谢 carma 开发人员 @RUrlusanswer

    问题是由于绑定模块,链接到 carma,被链接到尚未链接到 carma 的库。外部库(MRE 中的 mc)使用标准 malloc 分配内存,而 pybind11 在销毁时使用 Carma 的 free。不匹配导致 Windows 崩溃。

    Perhaps in the future there could be a more elegant solution,但目前的解决方法是在编译时将外部库链接到 carma,例如 target_link_libraries(mc PUBLIC armadillo carma),即使该外部库不包含或以任何明显方式使用 Carma。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-23
      相关资源
      最近更新 更多