编辑:这个问题已经过时了! numba现在支持Enum和namedtuple开箱即用,它们都为分组常量提供了合理的解决方案.
我在python中进行了一些比特转换,并希望用numba加速它.为此,我有很多常量整数值,我必须以一种可读的方式处理.我想将它们组合成类似于枚举的对象,在一个命名空间内具有所有常量,可以使用attribute-get运算符访问.当然我也想,numba了解那里发生的事情,以便通过jit编译保持高速度.我的第一次也是最天真的尝试看起来像这样:
class SomeConstantsContainer:
SOME_NAME = 0x1
SOME_OTHER_CONSTANT = 0x2
AND_ANOTHER_CONSTANT = 0x4
不幸的是,当我查看注释时,看起来numba并不理解值是常量,并且它总是回退到python对象上的慢对象访问.这就是注释所说的内容:
# $29.2 = global(SomeConstantsContainer: <class 'constants.SomeConstantContainer'>) :: pyobject
# $29.3 = getattr(attr=SOME_VARIABLE, value=$29.2) :: pyobject
我知道我总是可以回到这样的事情:
from numpy import np
SOME_STUPID_CONSTANT = np.int64(0x1)
ANOTHER_STUPID_CONSTANT = np.int64(0x2)
在这种情况下,jit编译器a)不需要查找容器的属性,并且b)确切地知道它必须处理普通整数.这样写是非常难看的.我可以将所有常量明确标记为整数,或让容器执行此操作.尽管如此,为了清晰起见,我真的想将容器中的常量分组,并且jit编译版本理解语法,而不是在每次使用常量时浪费时间进行一些慢速python属性查找.任何更好的想法,如何使第二种方法更像第一种方法,但保持高执行速度?是否有一些enum容器,numba理解,我错过了?
编辑:
使用新的枚举容器也无济于事:
@enum.unique
class SomeConstantsContainer(enum.IntEnum):
SOME_NAME = 0x1
SOME_OTHER_CONSTANT = 0x2
AND_ANOTHER_CONSTANT = 0x4
这给出了:
# $42.3 = global(SomeConstantsContainer: <enum 'SomeConstantsContainer'>) :: pyobject
# $42.4 = getattr(attr=SOME_OTHER_CONSTANT, value=$42.3) :: pyobject
解决方法:
一种不同的方法,但仍然具有包含变量的优点的方法是在包装函数中使用捕获的变量:
def make_numba_functions():
SOME_NAME = 0x1
SOME_OTHER_CONSTANT = 0x2
AND_ANOTHER_CONSTANT = 0x4
@jit
def f1(x,y):
useful code goes here
@jit
def f2(x,y,z):
some more useful code goes here
return f1, f2
f1,f2 = make_numba_functions()
显然,您可以将此类与使用类作为命名空间相结合,并将外部函数内的内容解压缩.
当我尝试使用一个非常基本的例子并使用inspect_types时,我得到了
$0.1 = freevar(A: 10.0) :: float64
(其中A只是我的常数).我怀疑这就是你想要的.我确实试着查看生成的汇编程序(os.environ [‘NUMBA_DUMP_ASSEMBLY’] =’1′)但是我在汇编程序中找不到相关的行并确认它符合你的要求是不够的.但是 – 它使用nopython = True进行编译,这至少表明它的工作效率很高.
有点黑客,但有一个非常有希望!