废弃通知

本节包含有关行为、功能和 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 模式下使用的适当声明。

废弃原因

“回退”功能反复给用户带来困惑,因为用户代码中看似无害的更改可能导致性能急剧变化,因为之前可能在 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

移除“回退”的另一个原因是,它让开发 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 编译器编译的代码内部进行的调用不兼容。这大大降低了 AOT 编译函数的使用价值。

  • numba.pycc 在某种程度上依赖于 setuptools(和 distutils),这是 Numba 正在努力减少的依赖,尤其考虑到 Python 3.12 中即将移除 distutils

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

  • numba.pycc 的用户数量据称相当少,这一点在 2022-10-04 的 Numba 公开会议和 #8509 问题中得到了体现。

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

影响示例

任何使用 numba.pycc 的源代码一旦该功能被移除,将无法工作。

时间表

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

  • 0.57.0 版本将发出待废弃警告。

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

  • 在完全移除前,至少有两个版本会给出显著的废弃警告。

建议

需要/依赖废弃行为的项目应将其对 Numba 的依赖固定在移除此行为之前的版本,或考虑遵循以下替换说明,以适应更改。

替换

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

CUDA 工具包 < 11.2 和计算能力 < 5.0 设备的废弃与移除

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

  • 对计算能力低于 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_CAPTURED_ERRORS 环境变量的使用。

废弃原因

此前,此变量允许控制 Numba 如何处理编译期间不继承自 numba.core.errors.NumbaError 的异常。默认的“旧式”行为是捕获并包装这些错误,通常会模糊原始异常。

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

旧样式已被移除,转而采用新行为。

影响

此废弃的影响仅限于正在扩展 Numba 功能的用户。

建议

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

时间表

  • 在 Numba 0.58 中:NUMBA_CAPTURED_ERRORS=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 的支持已移除。

内置 CUDA 目标的废弃

CUDA 目标现在在独立的包 numba-cuda 中维护,内置的 CUDA 目标已废弃。

废弃原因

CUDA 目标的开发已移至 numba-cuda 包,以便独立于 Numba 的开发进行。请参阅 内置 CUDA 目标的废弃和维护状态

影响

内置 CUDA 目标仍受 Numba 0.61 支持,并将至少在 Numba 0.62 中继续提供,但内置目标预计不会有新更改;错误修复和新功能将添加到 numba-cuda 中。任何使用 CUDA 目标的代码都不需要代码更改。

建议

使用 CUDA 目标时,用户应安装 numba-cuda 包。

使用 pip 安装 numba-cuda

pip install numba-cuda

使用 conda 安装 numba-cuda,例如从 conda-forge 频道:

conda install conda-forge::numba-cuda

使用 CUDA 目标的软件包维护者应将 numba-cuda 作为 numba 的额外依赖项添加,或者如果仅使用 CUDA 目标,则用 numba-cuda 替换 numba 依赖项。

时间表

  • 在 Numba 0.61 中:内置 CUDA 目标被废弃。

  • 在 Numba 0.62 中:当未安装 numba-cuda 包时使用 CUDA 目标将导致发出警告,提示安装 numba-cuda

  • 在 Numba 0.63 或更高版本中:内置 CUDA 目标将被移除,在没有 numba-cuda 包的情况下使用 CUDA 目标将引发错误。