通过pybind11从 C++绑定返回torch::Tensor时,Python会出现段错误

人工智能 2026-07-09

我有来自一个设备的数据,这些数据在Python中以数组的形式捕获,然后发送到一个C++绑定,以加速一些后处理,这些后处理必须先对数据进行正确格式化,并通过大量迭代任务将其转换为一个 torch.Tensor,再用 libtorch 进行最终转换成张量。我正在使用 pybind11 将C++函数绑定到Python。

我已经在C++实现中测试了这个函数(processing),它确实能够正确处理我的数据并创建具有正确形状和数据的张量,但在从绑定返回时,在C++ -> Python边界上出现了一些奇怪的内存问题。

基本情况是,我从C++获取最终张量并将其返回给Python。我把返回的张量赋给Python的一个变量,这样的做法可以工作。我能够访问张量的形状、数据类型等信息,但当我尝试访问任何类似数组的数据(通过下标访问、打印张量等)时,有时会发生段错误,具体取决于张量的形状。

据我所知,这与C++和 Python在调用析构函数时的内存管理问题有关。看起来Python基本上是在创建张量的浅拷贝,只能让我访问像形状这样的信息,但底层的实际数据却被C++销毁了,因此我本质上是在访问一个空指针(根据我的观察大致如此)。我尝试使用 py::return_value_policy::copy 来阻止这种情况,但没有效果(我尝试了所有返回值策略,但没有一个起作用)。

然而,这种行为非常奇怪,因为它只在某些张量尺寸下发生,就像一些非常特定的尺寸(见下文)。

这是我的C++函数签名和pybind模块:

// processing.cpp

#include <torch/torch.h>
#include <pybind11/pybind11.h>

namespace pybind11 as py;

torch::Tensor processing(py::array np_data, py::array dim){

    // Lots of working code here, converts numpy to uint8_t C array
    // to do necessary C stuff
    // ...

    // data_array is a C-style array of uint16_t
    auto options = torch::TensorOptions().dtype(torch::kUInt8);
    torch::Tensor output = torch::from_blob(data_array, options);
    finalOutput = torch::reshape(output, /*proper output shape*/);
    return finalOutput;
}

// Create python bindings
PYBIND11_MODULE(processing_ext, m, py::mod_gil_not_used()){
    m.def("processing", &processing,
        py::arg("batchOutputs"),
        py::arg("dim"),
        py::return_value_policy::copy
    );
}

我使用ninja按以下 setup.py 进行编译,因为cmake在 python3 setup.py build 上一直让人头疼:

# setup.py
from setuptools import setup
from torch.utils.cpp_extension import CppExtension, BuildExtension
import os

setup(
    name="processing_ext",
    ext_modules=[
        CppExtension(
            name="processing_ext",
            sources=["processing.cpp"],
            extra_compile_args=[],
        ),
    ],
    cmdclass={
        'build_ext': BuildExtension
    }
)

然后我在Python中调用我的绑定。

# test.py

import torch
import processing_ext # My c++ binding

# Dimensions
batch_size: int = 5
num_neurons: int = 32
data_len: int = 28
data_wid: int = 28

data: np.ndarray = ... # My data
output_shape: np.ndarray = np.ndarray([batch_size, num_neurons, data_len, data_wid])

# Call my C++ binding
output: torch.Tensor = processing_ext.processing(data, output_shape)

上述代码在上文列出的 test.py 指定的维度下可以工作,但并非在所有尺寸下都能工作。具体来说,保持其他维度不变时,当 num_neurons 处于1-5,或18-35之间时能工作,但在6-17或 36直到无穷之间则不行(这些区间均包含端点)。

有什么想法解释为什么在某些尺寸下会发生段错误,而在其他尺寸下不会?

我在Ubuntu 24.04.4上使用Python 3.12.3,搭配torch 2.7.1和 pybind11 2.11.2。

解决方案

from_blob的文档

/// Exposes the given `data` as a `Tensor` without taking ownership of the
/// original data. `sizes` should specify the shape of the tensor, `strides` the
/// stride in each dimension. The `deleter` function (a
/// `std::function<void(void*)>`) will be called on the `data` when the Tensor
/// data would normally be deallocated. The `TensorOptions` specify additional
/// configuration options for the returned tensor, such as what type to
/// interpret the `data` as.

很可能在执行 from_blob 之后需要对张量进行克隆,以确保创建了一个张量对象实例:

// Source - https://stackoverflow.com/q/79934444
// Posted by inventi, modified by community. See post 'Timeline' for change history
// Retrieved 2026-05-01, License - CC BY-SA 4.0

// processing.cpp

#include <torch/torch.h>
#include <pybind11/pybind11.h>

namespace pybind11 as py;

torch::Tensor processing(py::array np_data, py::array dim){

    // Lots of working code here, converts numpy to uint8_t C array
    // to do necessary C stuff
    // ...

    // data_array is a C-style array of uint16_t
    auto options = torch::TensorOptions().dtype(torch::kUInt8);
    torch::Tensor output = torch::from_blob(data_array, options).clone();
    finalOutput = torch::reshape(output, /*proper output shape*/);
    return finalOutput;
}

// Create python bindings
PYBIND11_MODULE(processing_ext, m, py::mod_gil_not_used()){
    m.def("processing", &processing,
        py::arg("batchOutputs"),
        py::arg("dim"),
        py::return_value_policy::copy
    );
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章