编辑:这个问题已经过时了! 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
容器也无济于事:
@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
答案 0 :(得分:2)
一种不同的方法,但仍然具有包含变量的优点的方法是在包装函数中使用捕获的变量:
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
编译,这至少表明它有效地工作。
有点黑客,但有希望的效果非常好!