通用 x64 处理器的 Numba 字节码生成?而不是第一个 运行 编译一个 SLOW @njit(cache=True) 参数

Numba bytecode generation for generic x64 processors? Rather than 1st run compiling a SLOW @njit(cache=True) argument

我有一个相当大的项目转换为 Numba,在 运行 #1 与 @nb.njit(cache=True, parallel=True, nogil=True) 之后,嗯,速度很慢运行 #1(比如编译后 15 秒对 0.2-1 秒)。我意识到它正在为我 运行 正在使用的特定 PC 优化编译字节码,但由于代码已分发给大量受众,我不希望它永远编译第一个 运行 在我们部署我们的模型之后。文档中未涵盖的是“通用 x64”cache=True 方法。我不关心代码在没有我的特定处理器的 PC 上是否有点慢,我只关心初始和后续 运行 时间很快,并且更希望它们没有区别如果我在部署时为@njit 函数分发一个缓存文件,那么利润会很大。

有谁知道这样的“通用”x64 实现是否可以使用 Numba?或者我们是否坚持使用慢速 运行 #1 然后是快速的?

如果您想了解更多详情,请发表评论;基本上它是大约 50 行代码函数,通过 Numba 进行 JIT 编译,然后 运行s 相当快地并行,没有 GIL。但如果代码可以跨多个处理器以通用形式工作,我愿意放弃一些极端的优化。在我工作的地方,PC 的先进程度可能会有很大差异。

我简要地查看了 Numba 函数的 AOT(提前)编译,但在这种情况下,这些函数有太多变量被更改我认为我需要数周时间才能正确装饰要编译的函数没有 Numba 依赖。我真的没有时间做 AOT,在 Cython 中重写整个算法会更有意义,这更像是 C/C++ 并且我想投入到这个项目中更耗时。不幸的是,(据我所知)目前还没有 Numba -> Cython 编译器项目。也许将来会有(那会很出色),但我目前还不知道有这样的项目。

很遗憾,您主要列出了所有当前可用的选项。可以缓存 Numba 函数并指定签名,以便执行急切编译(在函数定义时编译)而不是惰性编译(在第一次执行期间编译)。请注意,cache=True 标志仅用于在编译之前在 相同平台 上完成时跳过编译,而不是在多台机器之间共享代码。 AFAIK,Numba (LLVM-Lite) 使用的内部 JIT 不支持它。事实上,这正是 AOT compilation 的目的。话虽如此,AOT 编译需要提供签名(只要函数被编译,无论使用 approach/tool 是强制性的)并且它有相当大的限制(例如,目前不支持并行代码和快速数学)。请记住,Numba 的主要用例是 Just-in-Time 编译 而不是 ahead-of-time 编译。

关于您的 use-case,使用 Cython 似乎更有意义:对于某些通用平台,函数 pre-compiled 一次,编译的二进制文件可以直接提供给用户,不需要在目标机器上重新编译。


I don't care if the code is a little slower on a PC that doesn't have my specific processor.

好吧,关于您的代码,使用“通用”x86-64 代码可能要慢得多。主要原因在于使用了SIMD指令。事实上,x86-64 处理器都支持 SSE2 指令集,它提供了基本的 128 位 SIMD 寄存器,用于处理整数和 floating-point 数字。大约十年以来,x86 处理器支持 256 位 AVX 指令集,可显着加快 floating-point 计算速度。至少 7 年来,几乎所有主流 x86-64 处理器都支持主要加速整数计算的 AVX-2 指令集(尽管由于新功能它也有所改进 floating-point)。近十年来,FMA 指令集可以将使用 fuse-multiply 加法的代码加速 2 倍。最近的 Intel 处理器支持 512 位 AVX-512 指令集,它不仅可以将可执行的项数增加一倍每条指令计算,但也增加了许多有用的功能。最后,与过时的“通用”SSE2 指令集相比,使用新指令集 SIMD-friendly 代码可以快一个数量级。编译器(例如 GCC、Clang、ICC)旨在默认生成可移植代码,因此它们默认仅使用 SSE2。请注意,Numpy 已经使用此类“新”功能来加速许多功能(参见 sorts, argmin/argmax, log/exp 等)。