【问题标题】:Best way to handle multiple constants in a container with numba?使用 numba 在容器中处理多个常量的最佳方法?
【发布时间】:2015-04-22 11:12:13
【问题描述】:

编辑:这个问题已经严重过时了! numba 现在支持开箱即用的Enumnamedtuple,它们都为分组常量提供了合理的解决方案。


我正在 python 中进行一些位移,并希望使用 numba 加速它。为此,我有很多常量整数值,我必须以一种可能易于阅读的方式处理它们。我想将它们组合在一起成为类似枚举的对象,将所有常量都放在一个命名空间中,可以通过属性获取运算符进行访问。当然我也希望 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 属性查找。任何更好的想法,如何使第二种方法更像第一种方法,但保持高执行速度?是否有一些 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

【问题讨论】:

  • 很不清楚你在问什么。您是否试图在发出的 C/机器代码中将这些变量表示为 const 值?如果是这样,为什么?您无法在 Python 级别验证 const 正确性,因此这可能没有意义。也就是说,如果这些作为类属性存在,就像在您的示例代码中一样,那么任何人都可以在 Python before 中随时修改它们,并且调度 JIT 编译的函数调用。在 C 级别做 const-correctness 不会给你带来任何好处,我知道(但会对反例非常感兴趣)。
  • 我希望它们不是作为 python 对象被识别和处理,而是作为整数常量。我还希望 jit 编译器能够刮掉容器对象上的属性查找。
  • 注意:这个问题现在已经严重过时了。 numba 现在支持Enums 和namedtuples,它们都提供了更优雅的方式来回答这个问题。

标签: python enums constants numba


【解决方案1】:

另一种方法,但仍然具有包含变量的优点的方法是在包装函数中使用捕获的变量:

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 编译,这至少表明它正在有效地工作。

有点小技巧,但希望能很好地工作!

【讨论】:

  • 有趣的方法!这应该可以推广到每个常量集的装饰器中。确实很老套:)
  • 我确实考虑过装饰器,但不知道该怎么做。我有兴趣看到一个实现......
  • 我曾想过这样的事情,但最终并没有像我想象的那样成功:stackoverflow.com/questions/392349/…
  • 一个可行的选择是使用 Byteplay (wiki.python.org/moin/ByteplayDoc) 来修改函数字节码。从您的常量类中查找和替换属性查找可能不会太难(我猜它们都遵循相同的模式)。由于(我认为)Numba 读取字节码,它可能会起作用。不过,它确实依赖于看起来像未维护的库。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-22
  • 2021-12-31
  • 2010-09-20
  • 1970-01-01
  • 2020-10-18
  • 1970-01-01
相关资源
最近更新 更多