将 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 数组的主要内容,如果未能解决你的问题,请参考以下文章