弃用通知

本节包含关于弃用行为、功能和API的信息,这些行为、功能和API已经变得不受欢迎/过时。任何关于它们弃用时间表的信息以及更改背后的理由,连同示例,都会提供。然而,首先是一个关于如何抑制Numba可能引发的弃用警告的小节,以防止警告传播到正在使用Numba的代码中。

抑制弃用警告

所有 Numba 的弃用都是通过 NumbaDeprecationWarningNumbaPendingDeprecationWarning 发出的,要抑制这些警告的报告,可以使用以下代码片段:

from numba.core.errors import NumbaDeprecationWarning, NumbaPendingDeprecationWarning
import warnings

warnings.simplefilter('ignore', category=NumbaDeprecationWarning)
warnings.simplefilter('ignore', category=NumbaPendingDeprecationWarning)

上面使用的 action'ignore',还有其他可用的操作,更多信息请参见 警告过滤器 文档。

备注

强烈建议选择抑制这些警告的应用程序和库应将其 Numba 依赖项固定到合适的版本,因为其用户将不再意识到即将到来的不兼容性。

弃用 List 和 Set 类型的反射

反射 (reflection) 是 Numba 中用来描述确保编译代码对可变 Python 容器数据类型参数所做的更改在编译函数返回时对 Python 解释器可见的过程的术语。Numba 长期以来一直支持 listset 数据类型的反射,而正是这种反射的支持计划被弃用,以便用更好的实现来替代。

弃用原因

首先回顾一下,为了让 Numba 能够在 nopython 模式下编译一个函数,所有变量必须通过类型推断确定具体的类型。在简单的情况下,很明显如何将 nopython 模式下对容器的更改反映回原始的 Python 容器。然而,对于嵌套容器类型的复杂数据结构(例如,整数的列表的列表),要高效且一致地反映这些更改很快就变得不可能。经过多年的经验,很明显提供这种行为既充满困难,又常常导致代码性能不佳(所有反映的数据必须在调用时通过特殊 API 将数据转换为本地格式,然后在返回时转换回 CPython 格式)。因此,由于问题追踪器中报告的问题数量庞大,以及 typed.Dict``(类型化字典)的新方法进展良好,核心开发者决定弃用所述的 ``reflection 行为。

影响的示例

目前仅发出即将更改的警告。未来代码如:

from numba import njit

@njit
def foo(x):
    x.append(10)

a = [1, 2, 3]
foo(a)

将需要调整以使用 typed.List 实例,这个类型化容器与 类型化字典 功能相同。上述内容翻译的一个例子是:

from numba import njit
from numba.typed import List

@njit
def foo(x):
    x.append(10)

a = [1, 2, 3]
typed_a = List()
[typed_a.append(x) for x in a]
foo(typed_a)

有关 typed.List 的更多信息,请参阅 类型化列表。此功能在 0.47.0 版本周期中进行了进一步的可用性增强。

日程安排

此功能将按照此计划被移除:

  • 在版本0.44.0中将发出待弃用警告。

  • 在完全移除之前,将至少在两个版本中给予显著的通知。

推荐

需要/依赖于已弃用行为的项目应将其对 Numba 的依赖固定在删除此行为之前的版本,或考虑遵循将发布的替换说明,说明如何调整以适应变化。

预期的替换

如上所述,typed.List 将被用于在 list 的情况下允许类似反射的功能,typed.Set 将为 set 提供等效功能(尚未实现!)。这种方法的优点是:

  • 容器是有类型的意味着类型推断的工作量可以减少。

  • 嵌套容器(容器的容器的…)更容易支持。

  • 目前,在将数据转换为/从本地格式时产生的性能损失大部分被避免了。

  • Numba 的 typed.Dict 将能够使用这些容器作为值。

在使用 @jit 时,对象模式回退 行为的弃用

备注

此功能已在 0.59.0 版本中移除,请参阅下面的计划部分。

numba.jit 装饰器长期以来一直遵循首先尝试在 nopython 模式 下编译被装饰函数的行为,如果此编译失败,它将 回退 并尝试再次编译,但这次是在 对象模式 下。正是这种 回退 行为将被弃用,其结果将是 numba.jit 将默认在 nopython 模式 下编译,而 对象模式 编译将变为仅 选择加入

备注

在其他装饰器中使用 numba.jit 装饰器以提供一个简单的编译路径是相对常见的。由于这一变化,可能会从这些调用点引发弃用警告。为了避免这些警告,建议要么 抑制它们 如果应用程序不依赖于 对象模式 回退,要么检查装饰器的文档,看看如何将应用程序适当的选项传递给包装的 numba.jit 装饰器。Numba API 中的一个例子是 numba.vectorize。这个装饰器只是将关键字参数转发到内部的 numba.jit 装饰器调用点,例如 @vectorize(nopython=True) 将是 @vectorizenopython=True 模式下的适当声明。

弃用原因

fall-back 反复给用户带来了困惑,因为用户代码中看似无害的更改可能会导致性能的剧烈变化,例如,原本可能在 nopython 模式 下编译的代码可能会静默地切换到在 对象模式 下编译,例如:

from numba import jit

@jit
def foo():
    l = []
    for x in range(10):
        l.append(x)
    return l

foo()

assert foo.nopython_signatures # this was compiled in nopython mode

@jit
def bar():
    l = []
    for x in range(10):
        l.append(x)
    return reversed(l) # innocuous change, but no reversed support in nopython mode

bar()

assert not bar.nopython_signatures # this was not compiled in nopython mode

另一个移除 fall-back 的原因是,它对开发 Numba 的编译器工程师来说会造成混淆,因为它会导致内部状态问题,这些问题非常难以调试,并且使得操作编译器管道变得极其困难。

此外,长期以来,最佳实践一直被认为是将 numba.jit 装饰器中的 nopython 模式 关键字参数设置为 True,并且任何用户努力都应该投入到使代码在这种模式下工作,因为如果代码不在此模式下工作,收益非常小。结果是,随着 Numba 的发展,对象模式 在实践中使用的数量及其一般效用已经减少。可以注意到,通过 循环提升 的概念可以获得一些小的改进,但在实践中使用这种情况的情况很少,通常是使用较旧版本的 Numba 的遗留问题,在这种情况下,这种行为得到了更好的适应/推荐使用带有 回退@jit

影响的示例

目前,如果使用 @jit 装饰的代码使用了 回退 编译路径,则会发出即将更改的警告。未来的代码如:

@jit
def bar():
    l = []
    for x in range(10):
        l.append(x)
    return reversed(l)

bar()

将无法编译,会引发 TypingError

这一变化的另一个结果是,nopython 关键字参数将变得多余,因为 nopython 模式 将成为默认模式。因此,在此更改之后,提供 nopython=False 作为关键字参数将触发一个警告,指出隐式默认值已更改为 True。本质上,在移除此功能后,此关键字将不再起作用。

日程安排

此功能已根据此时间表移除:

  • 在版本 0.44.0 中发布了弃用警告。

  • 在 0.57.0 版本中已给出显著通知。

  • 该功能在 0.59.0 版本中已被移除。

推荐

需要/依赖于已弃用行为的项目应将其对 Numba 的依赖固定在移除此行为之前的版本。

关于适应计划中的弃用的一般建议:

当前使用 @jit 编译代码的用户可以提供 nopython=True 关键字参数,如果代码继续编译,则代码已经为这种变化做好了准备。如果代码无法编译,继续使用不带 nopython=True@jit 装饰器,并分析函数的性能。然后移除装饰器,再次检查函数的性能。如果没有 @jit 装饰器存在的好处,考虑移除它!如果有 @jit 装饰器存在的好处,为了未来兼容性,提供关键字参数 forceobj=True 以确保函数始终在 对象模式 下编译。

关于“循环提升”功能用户的建议:

如果需要使用带有循环提升的对象模式编译,应通过向 @jit 装饰器提供关键字参数 forceobj=Truelooplift=True 来明确声明。

对于设置 nopython=False 的用户的建议:

这基本上是在指定移除此功能之前的隐式默认值,要么移除关键字参数,要么将值更改为 True

弃用 generated_jit

顶级API函数 numba.generated_jit 提供了允许用户编写基于函数参数类型的不同实现的可JIT编译函数的功能。这是一个非常有用的概念,也是Numba内部实现的关键。

弃用原因

这一弃用有多个原因。

首先,generated_jit 打破了“JIT 透明性”的概念,因为如果 JIT 编译器被禁用,源代码的执行方式将与 JIT 编译器存在时不同。

其次,在内部,Numba 使用 numba.extending.overload 系列的装饰器来访问与 generated_jit 相当的功能。overload 系列的装饰器比 generated_jit 更强大,因为它们支持更多的选项,并且同时支持 CPU 和 CUDA 目标。实际上,generated_jit 的替代品已经存在,并且已经推荐和优选使用了一段时间。

第三,公共扩展API装饰器比 generated_jit 维护得更好。由于Numba资源有限,减少需要维护的功能重复部分将减轻这些资源的压力,这是一个重要的考虑因素。

有关 overload 装饰器家族的更多信息,请参阅 高级扩展API文档

影响的示例

任何使用 generated_jit 的源代码在功能被移除后将无法工作。

日程安排

此功能已根据此时间表移除:

  • 在版本 0.57.0 中发布了弃用警告。

  • 移除发生在版本 0.59.0。

推荐

需要/依赖于已弃用行为的项目应将其对 Numba 的依赖固定在移除此行为之前的版本,或考虑遵循以下替换说明,这些说明概述了如何调整以适应变化。

替换

overload 装饰器提供了一个替代 generated_jit 功能的方案。以下是一个从一种方式转换到另一种方式的示例。首先使用 generated_jit 装饰器定义一个类型特化的函数调度:

from numba import njit, generated_jit, types

@generated_jit
def select(x):
    if isinstance(x, types.Float):
        def impl(x):
            return x + 1
        return impl
    elif isinstance(x, types.UnicodeType):
        def impl(x):
            return x + " the number one"
        return impl
    else:
        raise TypeError("Unsupported Type")

@njit
def foo(x):
    return select(x)

print(foo(1.))
print(foo("a string"))

从概念上讲,generated_jit 类似于 overload,但在 generated_jit 中,重载的函数是被装饰的函数。以上面的例子为基础,调整它以使用 overload API:

from numba import njit, types
from numba.extending import overload

# A pure python implementation that will run if the JIT compiler is disabled.
def select(x):
    if isinstance(x, float):
        return x + 1
    elif isinstance(x, str):
        return x + " the number one"
    else:
        raise TypeError("Unsupported Type")

# An overload for the `select` function cf. generated_jit
@overload(select)
def ol_select(x):
    if isinstance(x, types.Float):
        def impl(x):
            return x + 1
        return impl
    elif isinstance(x, types.UnicodeType):
        def impl(x):
            return x + " the number one"
        return impl
    else:
        raise TypeError("Unsupported Type")

@njit
def foo(x):
    return select(x)

print(foo(1.))
print(foo("a string"))

此外,使用 generated_jit 来分派一些更基本类型的用户可能会发现 Numba 对 isinstance 的支持已经足够,例如:

@njit # NOTE: standard @njit decorator.
def select(x):
    if isinstance(x, float):
        return x + 1
    elif isinstance(x, str):
        return x + " the number one"
    else:
        raise TypeError("Unsupported Type")

@njit
def foo(x):
    return select(x)

print(foo(1.))
print(foo("a string"))

弃用 numba.pycc 模块

Numba 通过 numba.pycc 模块中的工具支持一定程度的提前编译(AOT)。这一功能对 Numba 项目非常重要,经过对当前方法可行性的评估,决定弃用它,转而开发新技术以更好地满足当前需求。

弃用原因

这一弃用有多个原因。

  • numba.pycc 工具创建的 C 扩展仅能从 Python 解释器中使用的符号,它们与使用 Numba 的 JIT 编译器编译的代码中的调用不兼容。这大大降低了预编译函数的实用性。

  • numba.pyccsetuptools``(以及 ``distutils)有一定的依赖,这是 Numba 正在努力减少的部分,特别是由于即将在 Python 3.12 中移除 distutils

  • numba.pycc 编译链在功能集方面与 Numba 的 JIT 编译器相比非常有限,它在声明和链接内部及外部库方面也存在许多技术问题。

  • numba.pycc 的用户数量被认为非常少,这一点在2022年10月4日的Numba公开会议讨论和问题 #8509 中有所体现。

  • Numba 项目正在 AOT 编译器领域进行新的创新,维护者认为将资源用于开发这些新功能比维护和发展 numba.pycc 更为有效。

影响的示例

任何使用 numba.pycc 的源代码在功能被移除后将无法工作。

日程安排

此功能将按照此计划被移除:

  • 在版本0.57.0中将发出待弃用警告。

  • 一旦开发出替代方案,将发出弃用警告。

  • 在完全移除之前,至少会在两个版本中给出弃用警告。

推荐

需要/依赖于已弃用行为的项目应将其对 Numba 的依赖固定在移除此行为之前的版本,或考虑遵循以下替换说明,这些说明概述了如何调整以适应变化。

替换

此功能的一个替代方案正在作为Numba 2023开发重点的一部分进行开发。在替代功能能够提供类似效用并提供升级路径之前,numba.pycc 模块不会被移除。在新技术被认为合适时,将发布替代说明。

弃用和移除 CUDA 工具包 < 11.2 及 CC < 5.0 的设备

  • 已移除对低于11.2版本的CUDA工具包的支持。

  • 对计算能力 < 5.0 的设备的支持已被弃用,并将在未来移除。

推荐

  • 对于计算能力为3.0和3.2的设备,需要使用Numba 0.55.1或更早的版本。

  • 应安装 CUDA 工具包 11.2 或更高版本。

日程安排

  • 在 Numba 0.55.1 中:对 CC < 5.0 和 CUDA 工具包 < 10.2 的支持已被弃用。

  • 在 Numba 0.56 中:移除了对 CC < 3.5 和 CUDA 工具包 < 10.2 的支持。

  • 在 Numba 0.57 中:移除了对 CUDA 工具包 10.2 的支持。

  • 在 Numba 0.58 中:移除了对 CUDA 工具包 11.0 和 11.1 的支持。

  • 在未来的版本中:将不再支持 CC < 5.0。

弃用旧式 NUMBA_CAPTURED_ERRORS

在Numba中,使用 NUMBA_CAPTURED_ERRORS=old_style 环境变量已被弃用。

弃用原因

之前,这个变量允许控制Numba在编译过程中如何处理不继承自``numba.core.errors.NumbaError``的异常。默认的“旧样式”行为是捕获并包装这些错误,通常会掩盖原始异常。

新的“new_style”选项将非``NumbaError``异常视为硬错误,不捕获直接传播。这区分了编译错误与编译期间意外的异常。

旧样式最终将被移除,以支持新的行为。用户应迁移到设置 NUMBA_CAPTURED_ERRORS='new_style' 以选择加入新的异常处理。这将在未来成为默认设置。

影响

这一弃用的影响只会影响到那些正在扩展 Numba 功能的人。

推荐

  • 扩展 Numba 的项目应设置 NUMBA_CAPTURED_ERRORS='new_style' 以进行测试,以查找编译期间引发非 NumbaError 异常的所有位置。

  • 修改任何引发非 NumbaError 的代码,以指示编译错误,改为引发 NumbaError 的子类。例如,不要引发 TypeError,而是引发 numba.core.errors.NumbaTypeError

日程安排

  • 在 Numba 0.58 中:NUMBA_CAPTURED_ERRORS=old_style 已被弃用。当使用 old_style 错误捕获时,将会引发警告。

  • 在 Numba 0.59 中:显式设置 NUMBA_CAPTURED_ERRORS=old_style 将引发弃用警告。

  • 在 Numba 0.60 中:NUMBA_CAPTURED_ERRORS=new_style 成为默认设置。

  • 在 Numba 0.61 中:将移除对 NUMBA_CAPTURED_ERRORS=old_style 的支持。