1

我正在使用pybind11.

numpy.array在其构造函数中接受 a,并获取指向其内部数据的指针。(它不复制数据)。

#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <iostream>

namespace py = pybind11;

struct Data
{
    Data(const py::array_t<double, py::array::c_style| py::array::forcecast>& arr)
        : p(arr.data())
    {
        std::cout << "arr=" << p    << std::endl;
        std::cout << "[0]=" << p[0] << std::endl;
    }
    const double* p;
};

我有另一个接受 a 的类const Data&,从而可以访问数组数据。

struct Manager
{
    Manager(const Data& data)
        : data_(data)
    {
        const double* p = data_.p;

        std::cout << "data.arr=" << p    << std::endl;
        std::cout << "data.[0]=" << p[0] << std::endl;
    }
    const Data& data_;
};

这里这两个类使用 pybind11 暴露给 python:

PYBIND11_MODULE(foo, m)
{
    py::class_<Data>(m, "Data")
        .def(py::init<const py::array_t<double, py::array::c_style| py::array::forcecast>&>());

    py::class_<Manager>(m, "Manager")
        .def(py::init<const Data&>());
}

这运作良好。我可以导入我的模块,Data从 a 创建一个实例numpy.array,然后将其传递给Manager

>>> import pandas
>>> import numpy
>>> import foo

>>> df = pandas.DataFrame(data = numpy.random.rand(990000, 7))
>>> d = foo.Data(df.values)
>>> c = foo.Manager(d)

我的脚本运行良好,您可以看到我的 C++ 代码访问numpy.array数据并将其地址和第一个元素打印到标准输出:

arr=0x7f47df313010
[0]=0.980507
data.arr=0x7f47df313010
data.[0]=0.980507

我创建以上所有内容都是为了创建一个MCVE来说明我在下面遇到的问题。

但是,现在我加载了我拥有的 pandas DataFrame pickle 文件(这里是有问题的 pickle 文件的下载链接):

>>> import pandas
>>> import foo

>>> df = pandas.read_pickle('data5.pk') 
>>> a = df.values
>>> d = foo.Data(a)
>>> c = foo.Manager(d)

我的 C++ 代码在尝试访问数组数据时崩溃。

这是标准输出:

arr=0x7f8864241010
arr[0]=7440.7
data.arr=0x7f8864241010
<dumps core>

所以指向数组的指针在 中是相同的Manager,但是尝试取消引用指针会导致 SEGV。

通过 valgrind 运行它,valgrind 报告Access not within mapped region at address 0x7f8864241010(即: 的地址numpy.array)。

Python 对我的 pickle 文件非常满意:

>>> import pandas

>>> df = pandas.read_pickle('data5.pk')
>>> df.shape
(990000, 7) 
>>> df
                  A             B             C            D            E  \
10000   7440.695240  15055.443905  14585.542158  3647.710616  8139.777981   
10001   7440.607794  15055.356459  14585.454712  3647.623171  8139.690536   
10002   7441.155761  15055.904426  14586.002679  3648.171138  8140.238503   
10003   7440.430209  15055.178874  14585.277127  3647.445585  8139.512950   
10004   7440.418058  15055.166724  14585.264977  3647.433435  8139.500800   
10005   7440.906603  15055.655268  14585.753521  3647.921979  8139.989344   
10006   7440.525167  15055.273832  14585.372085  3647.540543  8139.607908
...

我一生都无法弄清楚我的泡菜文件出了什么问题。

  • 我试过创建一个numpy.array酸洗,效果很好
  • 我试过创建一个pandas.DataFrame酸洗,效果很好
  • 我已经分割了我的“无效”数据框,我可以得到一个工作正常的子集

我的数据中有一些东西让 python 很高兴,但会导致 C++ 中的 SEGV。

我该如何诊断?

4

1 回答 1

2

泡菜不错。是你的代码错了。您获取指向数组数据的指针,而无需执行任何操作来确保该数据实际上与使用它的对象一样长。

您需要保留对数组的引用并执行关联的引用计数管理。pybind11 可能有某种机制来表示 Python 引用并为您处理引用计数。快速浏览一下文档,看起来您的代码可能应该采用array_t按值而不是 const 引用(因为 anarray_t已经代表 Python 引用),并将其存储到array_t实例变量中。

于 2018-07-13T17:29:41.677 回答