Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/349.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/maven/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何将C函数编译成numpy ufunc并动态加载?_Python_Numpy_Cython_Ctypes - Fatal编程技术网

Python 如何将C函数编译成numpy ufunc并动态加载?

Python 如何将C函数编译成numpy ufunc并动态加载?,python,numpy,cython,ctypes,Python,Numpy,Cython,Ctypes,我有一些自动生成C函数的Python代码。此函数接受一些double作为输入,并返回一个double,在此过程中调用C标准库中的各种函数 我想用它做的一件事是将其编译成一个numpy ufunc,并将其加载到正在运行的Python进程中。我只想让函数在其输入numpy数组上以合理的速度运行元素,比如numpy的minimum 我感到惊讶的是,我找不到清晰的说明或例子来说明如何做到这一点。Numpy对编写扩展有明确的说明,但不清楚如何将这些加载到当前的Python进程中。有了ctypes,我可以编

我有一些自动生成C函数的Python代码。此函数接受一些double作为输入,并返回一个double,在此过程中调用C标准库中的各种函数

我想用它做的一件事是将其编译成一个numpy ufunc,并将其加载到正在运行的Python进程中。我只想让函数在其输入numpy数组上以合理的速度运行元素,比如numpy的
minimum

我感到惊讶的是,我找不到清晰的说明或例子来说明如何做到这一点。Numpy对编写扩展有明确的说明,但不清楚如何将这些加载到当前的Python进程中。有了ctypes,我可以编译并加载我的函数,没有问题,但不清楚如何使它成为一个ufunc而不是一个普通的Python函数。Cython也可以这样做,如果我使用
pyximport
,它甚至可以为我构建共享库,这非常理想,因为这样我就可以分发它,而不用担心如何在另一个系统上构建C代码。但是,如何生成一个ufunc而不是一个普通函数仍然不清楚


TL;DR:我怎样才能获取一个简单的C函数,将它编译成一个ufunc,并动态加载它?越简单越好。

一个想法是使用numba编译c代码

例如,如果我们希望将numpy数组中每个元素的值加倍,即将以下C函数作为字符串:

double f(double a){
    return 2.0*a;
}
一个可能的解决方案是以下原型:

import numba as nb
import cffi

def create_ufunc(code):
    # 1. step: compile the C-code and load the resulting extension
    ffibuilder = cffi.FFI()
    ffibuilder.cdef("double f(double a);", override=True)
    built_module=ffibuilder.verify(source=code)
    fun = built_module.f

    # 2. step: create an ufunc out of the compiled C-function
    @nb.vectorize([nb.float64(nb.float64)])
    def f(x):
      return fun(x)
    return f
现在:

import numpy as np
a=np.arange(6).astype(np.float64)
my_f1=create_ufunc("double f(double a){return 2.0*a;}")
my_f1(a)
# array([  0.,   2.,   4.,   6.,   8.,  10.])
或者如果我们想与
10.0
相乘:

my_f2=create_ufunc("double f(double a){return 10.0*a;}")
# array([  0.,  10.,  20.,  30.,  40.,  50.])
显然,在展示可能性的同时,这个原型需要一些改进。例如,尽管结构紧凑,但已弃用,使用相同的代码调用两次
create\u ufunc
将导致警告

另一个问题:上面的版本没有在nopython模式下编译,尽管cffi起作用。不确定这里出了什么问题?请参阅下面的解决方法:在nopython模式下构建的更复杂版本

然而,这可能仍然是一个很好的起点


如果我们使用(
compile
)而不是(
verify
)API模式,似乎可以在Nopyton模式下编译numba:

import numba as nb
import cffi
import zlib
import importlib
import numba.cffi_support as nbcffi

def create_ufunc(code):
    # 1. step: compile the C-code and load the resulting extension
    # create a different so/dll for different codes
    # and load it
    module_name="myufunc"+str(zlib.adler32(code.encode('ascii')))
    ffibuilder = cffi.FFI()
    ffibuilder.cdef("double f(double a);", override=True)
    ffibuilder.set_source(module_name=module_name,source=code)
    ffibuilder.compile(verbose=True)
    loaded = importlib.import_module(module_name)


    # 2. step: create an ufunc out of the compiled C-function
    # out-of-line modules must be registered in numba:      
    nbcffi.register_module(loaded)
    fun = loaded.lib.f

    @nb.vectorize([nb.float64(nb.float64)], nopython=True)
    def f(x):
      return fun(x)
    return f
重要细节:

  • 每个
    代码都有一个新的扩展名(so/pyd文件)。我们通过传递的
    code
    的散列值来区分它们
  • 随着时间的推移,将会有相当多的
    myufuncXXXX。因此
    -文件,人们可以考虑实现一个类似于
    cffi使用的基础结构。验证
  • ffibuilder.compile(verbose=True)
    仅用于调试目的,可能
    verbose=False
    在发行版中更有意义

您找到了吗?您可能可以使用样板文件,从内部调用现有的本机C函数。@AndrasDeak我看到过,但看到了大约10个屏幕的样板文件代码,并被拒绝了。在该页面的某个地方,我将学习如何在不重新启动Python的情况下导入ufunc?@AndrasDeak我不想重新启动,因为C函数是根据用户输入自动生成的。我想无缝地编译、加载并“在后台”运行它,而用户不必担心.so文件之类的问题。缺少关于该主题的可用资源可能意味着您也可以尝试在numpy问题跟踪程序上询问同样的问题。如果这是一个没有文档记录的功能(足够好吗?),它可能是受欢迎的。如果您使用
cython
,您可以创建一个
。因此
文件可以随时导入。一个完整的
ufunc
接口可能需要做的工作比它的价值还多。这种基于numba的方法非常好!它似乎也可以无缝地与ctypes一起工作,使用nopython模式。(这节省了一个依赖项,但代价是生成过程不是自动的。)