将 C/C++ 向量快速转换为 Numpy 数组

Posted

技术标签:

【中文标题】将 C/C++ 向量快速转换为 Numpy 数组【英文标题】:Fast conversion of C/C++ vector to Numpy array 【发布时间】:2011-03-24 19:13:38 【问题描述】:

我正在使用 SWIG 将一些 C++ 代码粘合到 Python (2.6) 中,其中一部分粘合包括一段代码,用于将 C++ 端的大数据字段(数百万个值)转换为 Numpy 数组。我能想到的最好的方法是为类实现一个迭代器,然后提供一个 Python 方法:

def __array__(self, dtype=float):
    return np.fromiter(self, dtype, self.size())

问题是每个迭代器next 调用的成本都很高,因为它必须经过大约三到四个 SWIG 包装器。它需要的时间太长了。我可以保证 C++ 数据是连续存储的(因为它们存在于 std::vector 中),并且感觉 Numpy 应该能够将指针指向该数据的开头以及它包含的值的数量,并且直接阅读。

有没有办法将指向 internal_data_[0] 的指针和值 internal_data_.size() 传递给 numpy,以便它可以直接访问或复制数据而无需所有 Python 开销?

【问题讨论】:

【参考方案1】:

您需要定义__array_interface__() instead。这将让您直接传回指针和形状信息。

【讨论】:

你能提供更多的实际实现细节吗?还有一种方法可以做到这一点,而不必针对 Numpy 头文件编译我的项目?谢谢。 它还说这是一个遗留界面。 __array_interface__ 只是一个普通的字典,里面有普通的类型。无需使用任何 Numpy 标头进行编译。忽略称其为“遗留”的注释。我以为我已经删除了。如果你愿意,你可以实现 PEP 3118 缓冲区接口,但这更容易。【参考方案2】:

也许可以使用 f2py 代替 swig。尽管它的名字,它能够将 python 与 C 以及 Fortran 连接起来。见http://www.scipy.org/Cookbook/f2py_and_NumPy

优点是它会自动处理到 numpy 数组的转换。

两个警告:如果您还不了解 Fortran,您可能会觉得 f2py 有点奇怪;而且我不知道它与 C++ 的工作情况如何。

【讨论】:

感谢您的回复。我确实知道一些 FORTRAN,但我在代码中使用了很多 C++-y 功能:模板、typedef 等。我也不想引入另一个依赖项。 对 C++ 来说已经足够了。您可能不得不编写中间的普通 C 包装器,这可能会很痛苦。另一方面,它并不是真正的另一个依赖项,因为 f2py 是您已经在使用的 numpy 的一部分。你不需要 fortran 编译器。【参考方案3】:

如果您将向量包装在实现 Python Buffer Interface 的对象中,则可以将其传递给 numpy 数组进行初始化(参见 docs,第三个参数)。我敢打赌,这个初始化要快得多,因为它可以使用memcpy 来复制数据。

【讨论】:

感谢您的提示。您是否有任何使用 SWIG 中的 pybuffer_mutable_binary 或其他接口来实现 __buffer__ 接口的示例,例如浮点数? @Seth: 抱歉,我不能帮你。 所以看起来我必须从头开始为这个类手动实现整个缓冲区接口。 SWIG 仅提供读取其他缓冲区的功能,不提供导出缓冲区函数。【参考方案4】:

所以看起来唯一真正的解决方案是基于 pybuffer.i 的一些东西,它可以从 C++ 复制到现有的缓冲区中。如果将其添加到 SWIG 包含文件中:

%insert("python") %
import numpy as np
%

/*! Templated function to copy contents of a container to an allocated memory
 * buffer
 */
%inline %
//==== ADDED BY numpy.i
#include <algorithm>

template < typename Container_T >
void copy_to_buffer(
        const Container_T& field,
        typename Container_T::value_type* buffer,
        typename Container_T::size_type length
        )

//    ValidateUserInput( length == field.size(),
//            "Destination buffer is the wrong size" );
    // put your own assertion here or BAD THINGS CAN HAPPEN

    if (length == field.size()) 
        std::copy( field.begin(), field.end(), buffer );
    

//====

%

%define TYPEMAP_COPY_TO_BUFFER(CLASS...)
%typemap(in) (CLASS::value_type* buffer, CLASS::size_type length)
(int res = 0, Py_ssize_t size_ = 0, void *buffer_ = 0) 

    res = PyObject_AsWriteBuffer($input, &buffer_, &size_);
    if ( res < 0 ) 
        PyErr_Clear();
        %argument_fail(res, "(CLASS::value_type*, CLASS::size_type length)",
                $symname, $argnum);
    
    $1 = ($1_ltype) buffer_;
    $2 = ($2_ltype) (size_/sizeof($*1_type));

%enddef


%define ADD_NUMPY_ARRAY_INTERFACE(PYVALUE, PYCLASS, CLASS...)

TYPEMAP_COPY_TO_BUFFER(CLASS)

%template(_copy_to_buffer_ ## PYCLASS) copy_to_buffer< CLASS >;

%extend CLASS 
%insert("python") %
def __array__(self):
    """Enable access to this data as a numpy array"""
    a = np.ndarray( shape=( len(self), ), dtype=PYVALUE )
    _copy_to_buffer_ ## PYCLASS(self, a)
    return a
%


%enddef

然后您可以使用“Numpy”制作一个容器

%template(DumbVectorFloat) DumbVector<double>;
ADD_NUMPY_ARRAY_INTERFACE(float, DumbVectorFloat, DumbVector<double>);

然后在 Python 中,只需:

# dvf is an instance of DumbVectorFloat
import numpy as np
my_numpy_array = np.asarray( dvf )

这只有单个 Python C++ 转换调用的开销,而不是典型的长度为 N 数组的 N。

我的PyTRT project at github 的一部分是此代码的稍微完整的版本。

【讨论】:

以上是关于将 C/C++ 向量快速转换为 Numpy 数组的主要内容,如果未能解决你的问题,请参考以下文章

如何将包装为字符串的向量转换为熊猫数据框中的numpy数组?

Numpy:将标签转换为索引

如何将 NumPy 数组标准化为单位向量?

如何将 NumPy 数组标准化为单位向量?

python怎么将数组转换为矩阵

如何快速将返回的 Python-in-Lua numpy 数组转换为 Lua Torch 张量?