Python 在项目中将numba功能拆分为单独的模块进行打包

Python 在项目中将numba功能拆分为单独的模块进行打包,python,python-3.x,numba,python-packaging,Python,Python 3.x,Numba,Python Packaging,我的项目中有几个模块,每个模块都包含一些numba函数。我知道在第一次导入时,函数会被编译。 我现在才注意到的是,即使我只从一个模块中导入一个函数,似乎所有函数都会被编译,因为导入所需的时间是相同的 我希望实现一种更细粒度的方法,因为对于某些应用程序,您实际上只需要一个函数,因此编译所有函数都是浪费时间 为此,我将函数拆分为单独的模块,如下所示: 项目/ |--src/ ||--uuu init_uuuuuuuuuuuuuuuuuy.py ||--fun1.py ||--fun2.py ||--

我的项目中有几个模块,每个模块都包含一些
numba
函数。我知道在第一次导入时,函数会被编译。 我现在才注意到的是,即使我只从一个模块中导入一个函数,似乎所有函数都会被编译,因为导入所需的时间是相同的

我希望实现一种更细粒度的方法,因为对于某些应用程序,您实际上只需要一个函数,因此编译所有函数都是浪费时间

为此,我将函数拆分为单独的模块,如下所示:

项目/
|--src/
||--uuu init_uuuuuuuuuuuuuuuuuy.py
||--fun1.py
||--fun2.py
||--fun3.py
||--fun4.py
|  |-- ...
带有
\uuuu init\uuuuu.py
包括

from.fun1导入fun1
from.fun2导入fun2
...
因此,它们可以像
一样从src import fun1导入

这似乎工作正常,但在导入级别有一些重复,例如每个函数都需要来自numba import jit的
,其中一些函数需要来自numpy import Zero的
,等等

所以我的问题是,这是一种好方法,还是有更好的方法来打包许多
numba
函数

编辑: 将所有导入语句放入
\uuuu init\uuuuu.py
显然意味着导入一个函数后,所有函数都会被编译,因此没有任何收益

我仍然可以导入如下函数

从src.fun1导入fun1

这似乎有效。但是语法有点笨拙。

有趣的问题-你本质上是在问如何延迟函数的定义,直到它被显式导入。 我认为最好的方法是像您所说的那样,使用src.fun1 import fun1中的
并为每个文件提供一个函数

我认为在同一个文件中有多个函数时实现这一点可能非常棘手,因此我将问题放宽到“如何延迟函数的定义,直到它显式地调用(未导入)”

平凡解 一种简单的方法是将函数包装在一个伪外部函数中

这并不是我们想要的,因为对
fun1
的后续调用将导致重新创建内部函数和
numba.jit
装饰器,并且需要重新编译

#main.py
#这让我们看到numba何时编译。
#看https://numba.pydata.org/numba-doc/dev/reference/envvars.html
导入操作系统
操作系统环境[“NUMBA_调试_前端”]=“1”
进口fun1
打印(“注意fun1还没有numba调试输出”)
打印(“fun1结果为”,fun1.fun1(1,2))
打印(“fun1结果为”,fun1.fun1(2,1))
print(“注意函数编译了两次:(”)
#fun1.py
进口麻木
#天真地将fun1包装到另一个函数中,这样它就只被声明了
#当调用外部函数时。
def fun1(*args,**kwargs):
@jit(“float32(float32,float32)”,cache=False)#无缓存,用于调试
定义1(a,b):
返回a+b
返回函数1(*args,**kwargs)
使用装饰器的更高级解决方案 简单的解决方案是将您的函数包装到另一个函数中…闻起来很像一个装饰器

我创建了一个decorator(外部decorator),它将另一个decorator(内部decorator)作为输入。 外部修饰符仅在第一次调用函数时应用内部修饰符(
numba.jit
),然后在后续调用中重新使用内部修饰符函数

#main.py
#这让我们看到numba何时编译。
#看https://numba.pydata.org/numba-doc/dev/reference/envvars.html
导入操作系统
操作系统环境[“NUMBA_调试_前端”]=“1”
进口fun2
打印(“注意fun2还没有numba调试输出”)
打印(“fun2结果为”,fun2.fun2(3,4))
打印(“fun2结果为”,fun2.fun2(5,6))
打印(“注意,函数只编译了一次:))
#fun2.py
进口麻木
从functools导入包装
def延迟(内部装饰器):
def_延迟(f):
内部装饰=无
@包装(f)
def包装(*args,**kwds):
非局部内环
如果“内部”为“无”:
内部装饰=内部装饰器(f)
返回内部装饰(*args,**kwds)
返回包装器
返回延迟
@延迟(numba.jit(“float32(float32,float32)”,cache=False))
def fun2(a、b):
返回a*b
我知道在第一次导入时,函数会被编译

这不一定是真的。仅当使用显式签名声明函数时发生

使用显式签名声明函数有两个后果:

  • 函数由decorator编译(声明或导入时)
  • 不允许其他函数专门化
如果不需要禁止其他专门化,只需删除函数签名,它们将在每次专门化的第一次执行时编译


您可以通过在
numba.core.dispatcher.dispatcher.compile()
方法中放置断点来检查函数何时被编译。

一旦解释器加载了一个模块(例如numba或numpy),将其导入代码中的某个地方并不需要花费太多成本:缓存中的
sys.modules
不会重新加载该模块。因此,在每个模块中一次又一次地导入并不会对性能造成影响。+1。。。好吧,你的“微不足道的解决方案”显然是不可取的。我不确定你的“高级解决方案”是否能回答我最初的问题,而且这种双重包装确实是一种“东西”。但是您可能刚刚回答了一个我还没有问过的问题-我正计划在方法中导入函数,在该方法中它是防止过早编译所必需的,尽管我对此并不完全满意。这样我就可以导入t