缓存注意事项

Numba 支持将编译后的函数缓存到文件系统,以便将来再次使用这些函数。

实现

缓存通过保存编译后的*目标代码*实现,即可执行代码的 ELF 对象。使用*目标代码*可以使缓存的函数开销最小化,因为无需再次编译。缓存数据保存在缓存目录下(参见 NUMBA_CACHE_DIR)。缓存的索引存储在 .nbi 文件中,每个函数一个索引,其中列出了为该函数编译的所有重载签名。*目标代码*存储在扩展名为 .nbc 的文件中,每个重载一个文件。这两种文件中的数据都使用 pickle 进行序列化。

可缓存性要求

开发人员应注意函数可被缓存的各项要求,以确保他们正在开发的功能与缓存兼容。

可缓存函数的要求

  • LLVM 模块必须是*自包含的*,这意味着它不能在不链接到其他编译单元的情况下依赖它们。

  • 唯一允许的外部符号来自 NRT 或来自系统库(例如 libc 和 libm)的其他常见符号。

调试注意事项

  • 在 LLVM IR 中查找 inttoptr 的用法,或在 Python 的降低代码中查找 target_context.add_dynamic_add()。它们表示可能使用了运行时地址。并非所有用法都有问题,有些是必需的。只有将常量整数转换为指针才会影响缓存。

  • 动态地址或动态符号的误用很可能导致段错误(segfault)。

  • 链接顺序很重要,因为未使用的符号在链接后会被丢弃。链接应从依赖图的叶节点开始。

与缓存兼容的功能

以下功能已明确验证与缓存兼容。

  • ufuncs 和 gufuncs 适用于 cpuparallel 目标

  • 并行加速器功能(即 parallel=True

缓存限制

这是已知的缓存限制列表

  • 缓存失效无法识别在不同文件中定义的符号的更改。

  • 全局变量被视为常量。缓存将记住编译时的全局变量值。在缓存加载时,缓存的函数不会重新绑定到全局变量的新值。

缓存共享

在不同的机器上共享和重用缓存目录中的内容是安全的。缓存在编译时会记住 CPU 型号和可用的 CPU 功能。如果 CPU 型号和 CPU 功能不完全匹配,缓存内容将不被考虑。(另请参见 NUMBA_CPU_NAME

如果缓存在网络文件系统上共享,则只有在文件替换操作对于文件系统是原子性的情况下,缓存的并发读写才是安全的。Numba 总是首先写入一个唯一的临时文件,然后用该临时文件替换目标缓存文件路径。Numba 对丢失的缓存文件和丢失的缓存条目具有容错性。

缓存清除

当对应的源文件被修改时,缓存会失效。但是,有时需要手动清除缓存目录。例如,编译器的更改将不会被识别,因为源文件未被修改。

要清除缓存,可以直接删除缓存目录。

在 Numba 应用程序运行时删除缓存目录可能会在编译时引发 OSError 异常。