将大型复杂数组从Python传递到C ++-我最好的选择是什么?

发布于 2021-01-29 16:13:48

2017/06/13编辑:我尝试按照建议的方式使用boost,但是花了3天以上的时间尝试使其进行编译和链接,然后失败了,我认为这种愚蠢的痛苦方式可能是最快且痛苦程度最低的方式。
..所以现在我的代码只是保存了C ++然后读取的一堆硕大的文本文件(拆分数组以及文件中数字的复数/虚数部分)。优雅…不…有效…是。


我有一些科学代码,当前使用Python编写,但由于循环内的数字3d集成步骤而使其速度变慢。为了克服这个问题,我在C
++中重写了这一特定步骤。(Cython等不是选项)。

长话短说:我想尽可能方便和轻松地将数个非常大的复数数组从python代码传输到C
++集成器。我可以手动使用文本或二进制文件来完成此操作,但是在进行此操作之前,我想知道是否有更好的选择?

我正在使用Visual Studio for C ++和Anaconda for python(不是我的选择!)

是否有任何文件格式或方法可以快速方便地从python保存复数数组,然后在C ++中重新创建它?

非常感谢,本

关注者
0
被浏览
44
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

    我多次使用的简单解决方案是将您的“ C ++端”构建为dll(= Linux / OS X上的共享对象),提供一个简单的, 类似于C的
    入口点(直整数,指针和连字符,没有STL内容) )并将数据通过ctypes

    这避免了boost / SIP / Swig /
    …构建梦night,可以保持零拷贝(使用ctypes可以将直指针指向numpy数据),并允许您做任何您想做的事情(尤其是在构建方面-
    没有friggin的distutils,没有提升,什么也没有-用可以在C 方面构建类似于C的dll的方式进行构建。让您的C
    算法可以从其他语言调用(这几乎是任何一种语言都可以与C库进行交互的方式),这也具有很好的副作用。


    是一个快速的人工示例。C ++方面只是:

    extern "C" {
    double sum_it(double *array, int size) {
        double ret = 0.;
        for(int i=0; i<size; ++i) {
            ret += array[i];
        }
        return ret;
    }
    }
    

    必须将其编译为dll(在Windows上)或.so(在Linux上),以确保导出sum_it功能(使用gcc自动运行,需要.def使用VC
    ++的文件)。

    在Python方面,我们可以使用类似

    import ctypes
    import os
    import sys
    import numpy as np
    
    path = os.path.dirname(__file__)
    cdll = ctypes.CDLL(os.path.join(path, "summer.dll" if sys.platform.startswith("win") else "summer.so"))
    _sum_it = cdll.sum_it
    _sum_it.restype = ctypes.c_double
    
    def sum_it(l):
        if isinstance(l, np.ndarray) and l.dtype == np.float64 and len(l.shape)==1:
            # it's already a numpy array with the right features - go zero-copy
            a = l.ctypes.data
        else:
            # it's a list or something else - try to create a copy
            arr_t = ctypes.c_double * len(l)
            a = arr_t(*l)
        return _sum_it(a, len(l))
    

    确保数据正确封送;然后调用该函数就很简单

    import summer
    import numpy as np
    # from a list (with copy)
    print summer.sum_it([1, 2, 3, 4.5])
    # from a numpy array of the right type - zero-copy
    print summer.sum_it(np.array([3., 4., 5.]))
    

    请参阅ctypes文档以获取有关如何使用它的更多信息。另请参阅numpy中的相关文档


    对于复数,情况要稍微复杂一些,因为ctypes中没有内置函数。如果我们想std::complex<double>在C
    端使用(可以保证可以很好地与numpy复杂布局配合使用,即两个双精度序列),则可以将C
    端写为:

    extern "C" {
    std::complex<double> sum_it_cplx(std::complex<double> *array, int size) {
        std::complex<double> ret(0., 0.);
        for(int i=0; i<size; ++i) {
            ret += array[i];
        }
        return ret;
    }
    }
    

    然后,在Python端,我们必须复制c_complex布局以获取返回值(或能够构建没有numpy的复杂数组):

    class c_complex(ctypes.Structure):
        # Complex number, compatible with std::complex layout
        _fields_ = [("real", ctypes.c_double), ("imag", ctypes.c_double)]
    
        def __init__(self, pycomplex):
            # Init from Python complex
            self.real = pycomplex.real
            self.imag = pycomplex.imag
    
        def to_complex(self):
            # Convert to Python complex
            return self.real + (1.j) * self.imag
    

    继承自ctypes.Structure启用ctypes编组魔术,这是根据_fields_成员执行的;构造函数和其他方法只是为了在Python方面易于使用。

    然后,我们必须告诉ctypes返回类型

    _sum_it_cplx = cdll.sum_it_cplx
    _sum_it_cplx.restype = c_complex
    

    最后以与上一个类似的方式编写我们的包装器:

    def sum_it_cplx(l):
        if isinstance(l, np.ndarray) and l.dtype == np.complex and len(l.shape)==1:
            # the numpy array layout for complexes (sequence of two double) is already
            # compatible with std::complex (see https://stackoverflow.com/a/5020268/214671)
            a = l.ctypes.data
        else:
            # otherwise, try to build our c_complex
            arr_t = c_complex * len(l)
            a = arr_t(*(c_complex(r) for r in l))
        ret = _sum_it_cplx(a, len(l))
        return ret.to_complex()
    

    如上测试

    # from a complex list (with copy)
    print summer.sum_it_cplx([1. + 0.j, 0 + 1.j, 2 + 2.j])
    # from a numpy array of the right type - zero-copy
    print summer.sum_it_cplx(np.array([1. + 0.j, 0 + 1.j, 2 + 2.j]))
    

    产生预期的结果:

    (3+3j)
    (3+3j)
    


知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看