为什么在 pybind11 中使用犰狳矩阵会导致堆损坏?
Why might I get heap corruption using Armadillo matrices with pybind11?
我已经为此工作了几个星期,无法在我的代码库之外制作可重现的示例。这就是为什么我需要帮助!我不确定这是否是 pybind11 或 Armadillo 的问题。 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 class 绑定代码。我不知道可能缺少什么,所以我的 MRE 没有重现错误。
怪事
- 仅在从 pybind11 而非纯 C++ 调用时发生
- 仅当包含矩阵的 class 的构造函数位于单独编译的源文件中时才会发生。当构造函数在头文件中时不会发生。
- 仅在将非恒等式(例如
np.ones
但不是 np.eye
)矩阵分配给对象的矩阵成员变量时发生。 但这只发生在我的源代码中的 class,而不是我创建的较小的测试 class!
- 仅当 A 大到足以让 Armadillo 获取内存而不是使用本地内存时才会发生
- 在分配之前用
set_size()
设置 A 的大小并不能解决问题
- 这似乎是犰狳特有的,当犰狳释放内存时(使用
ARMA_EXTRA_DEBUG
确认),因为当我使用另一个 class 动态分配的内存时,我没有得到类似的错误向量
当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
感谢 carma 开发者,@RUrlus, for the answer:
问题是由于绑定模块 linked 到 carma,被 linked 到一个没有被 linked 到 carma 的库。外部库(MRE 中的 mc)使用标准 malloc
分配内存,而 pybind11 在销毁时使用 Carma 的 free
。不匹配导致 Windows.
崩溃
Perhaps in the future there could be a more elegant solution,但目前的解决方法是在编译时将 link 外部库添加到 carma,例如 target_link_libraries(mc PUBLIC armadillo carma)
即使该外部库不包含或使用Carma 以任何明显的方式。
我已经为此工作了几个星期,无法在我的代码库之外制作可重现的示例。这就是为什么我需要帮助!我不确定这是否是 pybind11 或 Armadillo 的问题。 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 class 绑定代码。我不知道可能缺少什么,所以我的 MRE 没有重现错误。
怪事
- 仅在从 pybind11 而非纯 C++ 调用时发生
- 仅当包含矩阵的 class 的构造函数位于单独编译的源文件中时才会发生。当构造函数在头文件中时不会发生。
- 仅在将非恒等式(例如
np.ones
但不是np.eye
)矩阵分配给对象的矩阵成员变量时发生。 但这只发生在我的源代码中的 class,而不是我创建的较小的测试 class! - 仅当 A 大到足以让 Armadillo 获取内存而不是使用本地内存时才会发生
- 在分配之前用
set_size()
设置 A 的大小并不能解决问题 - 这似乎是犰狳特有的,当犰狳释放内存时(使用
ARMA_EXTRA_DEBUG
确认),因为当我使用另一个 class 动态分配的内存时,我没有得到类似的错误向量
当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
感谢 carma 开发者,@RUrlus, for the answer:
问题是由于绑定模块 linked 到 carma,被 linked 到一个没有被 linked 到 carma 的库。外部库(MRE 中的 mc)使用标准 malloc
分配内存,而 pybind11 在销毁时使用 Carma 的 free
。不匹配导致 Windows.
Perhaps in the future there could be a more elegant solution,但目前的解决方法是在编译时将 link 外部库添加到 carma,例如 target_link_libraries(mc PUBLIC armadillo carma)
即使该外部库不包含或使用Carma 以任何明显的方式。