【问题标题】:Fast conversion of C/C++ vector to Numpy array将 C/C++ 向量快速转换为 Numpy 数组
【发布时间】: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 开销?

【问题讨论】:

    标签: c++ python arrays numpy swig


    【解决方案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 的一部分是此代码的稍微完整的版本。

    【讨论】:

      猜你喜欢
      • 2011-02-06
      • 2023-03-12
      • 2015-08-25
      • 1970-01-01
      • 2022-12-11
      • 2023-04-07
      • 1970-01-01
      • 1970-01-01
      • 2013-06-10
      相关资源
      最近更新 更多