您好, 欢迎来到 !    登录 | 注册 | | 设为首页 | 收藏本站

Cython模块中的加载与链接

Cython模块中的加载与链接

调用cdef-function或多或少地相当于跳转到内存中的一个地址-应该从中读取/执行命令。问题是如何提供此地址。在某些情况下,我们需要考虑:

A.内联函数

这些函数代码是内联的,或者函数的定义在同一个转换单元中,因此链接器在链接时(甚至在编译时甚至是编译器)都知道地址,而无需其他库。

一个示例是仅标头库。

后果:中仅应提供包含路径setup.py

B.静态链接

我们需要的定义/功能在另一个翻译单元/库中-跳转的目标地址是在链接时计算的,此后不能再更改。

一个示例是添加到扩展定义中的其他c / cpp文件或静态库。

结果:静态库应添加到setup.py,即库路径和库名称以及包含路径。

C.动态链接

共享库/共享库中提供了必要的功能。要跳转到的地址是在运行时从加载程序中计算得出的,可以在程序启动时通过交换已加载的共享对象来替换。

例如stdlibc (通常由g 自动添加)或libm,它们不会由gcc自动添加链接器命令中。

结果:动态库应添加到setup.py,即库路径和库名称,可能是r-path + include路径。必须在运行时提供共享的对象/ dll。可以在此SO-post中找到更多(使用动态库)有关Cython / Python的信息。

D.通过指针调用

仅当我们通过函数名称调用函数时,才需要链接器。如果通过函数指针调用它,则不需要链接器/加载器,因为函数的地址是已知的-函数指针中的值。

示例:Cython生成的模块使用此机制来启用对通过pxd-file导出的cdef函数的访问。它创建一个__pyx_capi__功能指针的数据结构(将其存储为模块本身的变量),一旦通过ldopen(或Windows的任何等效方法)加载了so / dll,该指针便由加载器填充。字典中的查找仅在模块加载且函数地址被缓存时才发生一次,因此在运行时的调用几乎没有开销。

我们可以检查它,例如通过

#foo.pyx:
cdef void doit():
    print("doit")
#foo.pxd
cdef void doit()

>>> cythonize -3 -i foo.pyx
>>> python -c "import foo; print(foo.__pyx_capi__)" 
{'doit': <capsule object "void (void)" at 0x7f7b10bb16c0>}

现在,cdef从另一个模块调用函数只是跳转到相应的地址。

结果:我们需要导入所需的功能

Numpy有点复杂,因为它使用A和D的复杂组合以便将符号的解析推迟到运行时,因此在链接时(但在运行时!)不需要共享对象/ dll。

可以直接使用numpy -pxd文件中的某些功能,因为它们是内联的(甚至只是定义了),例如PyArray_NDIM,基本上所有来自的功能ndarraytypes.h。这就是为什么可以毫不费力地使用cython的ndarray的原因。

例如,ndarrayobject.h如果没有np.import_array()在初始化步骤中调用,则无法访问其他功能(基本上是from的所有功能)PyArray_FromAny。为什么?

答案是在报头__multiarray_api.h被包括ndarrayobject.h,但不能在找到的git存储库,因为它是生成的安装信息,其中的定义期间PyArray_FromAny可以查找:

...
static void **PyArray_API=NULL; //usually...
...
#define PyArray_CheckFromAny \
        (*(PyObject * (*)(PyObject *, PyArray_Descr *, int, int, int, PyObject *)) \
         PyArray_API[108])
...

PyArray_CheckFromAny不是函数名称,而是保存在中的函数指针的定义PyArray_API,该指针在NULL首次加载模块时未初始化(即)。顺便说一句,还有一个(私有)函数称为PyArray_CheckFromAny,它实际上是函数指针指向的内容-并且由于公共版本是一个定义,因此链接时不会发生名称冲突…

难题的最后一部分-函数_import_array(或多或少地是后面的工作马np.import_array)是内联函数(情况A),因此仅需要include路径即可使用它。

_import_array使用与Cython类似的方法来__pyx_capi__获取函数指针:调用了该字段_ARRAY_API,可以通过以下方法检查该字段:

>>> import numpy.core._multiarray_umath as macore
>>> macore._ARRAY_API
<capsule object NULL at 0x7f17d85f3810>

有关如何PyArray_API初始化的更多信息,可以在我的SO解答中找到。

但是,当使用from中的功能时numpy/math.pxd,必须静态链接numpy的数学库(例如,参见此SO问题)。

其他 2022/1/1 18:35:08 有462人围观

撰写回答


你尚未登录,登录后可以

和开发者交流问题的细节

关注并接收问题和回答的更新提醒

参与内容的编辑和改进,让解决方法与时俱进

请先登录

推荐问题


联系我
置顶