浏览器中的 Numba:在 JupyterLite 中开启全新科学 Python 技术栈

Lobsters Hottest 工具

摘要

本文宣布了 Numba JIT 编译器的首个可运行版本,该版本可完全在浏览器中运行,借助 JupyterLite 和 WebAssembly,比标准 Python 快约 250 倍,并可在静态网站上启用完整的科学 Python 技术栈。

<p><a href="https://lobste.rs/s/l3ephx/numba_browser_unlocking_new_scientific">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/08/12 14:23

# Numba 在浏览器中:在 JupyterLite 中解锁新的科学 Python 技术栈 来源:https://notebook.link/blog/numba-in-the-browser/ ## 一个 JIT 编译器——以及它的生态系统——完全在 Web 浏览器中运行(https://notebook.link/blog/numba-in-the-browser/#a-jit-compilerand-its-ecosystemrunning-entirely-in-the-web-browser) 科学家、学生和工程师使用 Jupyter notebook 以交互方式探索想法:编写一小段代码,执行它,检查结果,然后继续。传统上,每个这样的 notebook 都需要一个运行在服务器或用户机器上的 Python 进程。 JupyterLite 改变了这种模式。它的内核通过 WebAssembly 在 Web 浏览器中本地运行,因此一个静态网站就可以提供完整的计算环境,而无需为每个用户分配服务器。这使得 notebook 可以更容易、更廉价地大规模共享,无论是用于文档、教育还是交互式演示。 然而,基于浏览器的科学 Python 生态系统中一直缺少一个重要部分:**Numba**。 今天,我们很高兴地分享第一个在浏览器中通过 JupyterLite 和 emscripten-forge 完全运行的 Numba JIT 编译器可用版本! JupyterLite 中的 Numba 基准测试 *Numba 在 JupyterLite 中运行,显示出比标准 Python 快 249 倍的速度。* 在这个例子中,Numba 在 WebAssembly 中提供了大约 250 倍的加速,而原生环境约为 90 倍。相对更大的增益使 Numba 在浏览器中尤其引人注目,因为在浏览器中绕过 Python 解释器开销可以产生更大的影响。 这意味着一个 Python 函数可以被转换为 Numba 的中间表示(IR)、进行类型推断、降级为 LLVM IR、编译为 WebAssembly、动态链接并执行——所有这些都无需远程 Python 服务器。 在此试用:JupyterLite 中的 Numba 及其生态系统(https://notebook.link/@anutosh491/numba-ecosystem)。 ## 一个长期的请求(https://notebook.link/blog/numba-in-the-browser/#a-long-standing-request) 对 WebAssembly 中 Numba 支持的讨论已经持续了很多年。这个请求早在 2018 年就出现在 Numba 项目中,见numba/numba#3284(https://github.com/numba/numba/issues/3284),而 Pyodide 社区则在pyodide/pyodide-recipes#192(https://github.com/pyodide/pyodide-recipes/issues/192)中跟踪打包挑战。 困难并不仅仅在于 Numba 从未为 WebAssembly 打包过。Numba 是一个编译器,其执行模型依赖于 llvmlite 和 LLVM。在原生平台上,生成的机器代码可以被放入可执行内存并立即调用。浏览器故意不允许应用程序以这种方式创建或修改可执行内存。 因此,将 Numba 带到浏览器需要的不仅仅是调整构建系统或修复一些平台检查。我们需要一个支持 WebAssembly 的 llvmlite 执行引擎,一种在运行中的浏览器进程内调用 LLVM 链接器的方法,以及支持将生成的代码动态加载到持久 Python 运行时中的能力。 幸运的是,这是我们以前遇到过的一个问题。 ## 从交互式 C++ 到 llvmlite(https://notebook.link/blog/numba-in-the-browser/#from-interactive-c-to-llvmlite) 我们早期在JupyterLite 中的 Xeus-Cpp(https://medium.com/jupyter-blog/c-in-jupyter-interpreting-c-in-the-web-c9d93542f20b)方面的工作将 Clang-Repl C++ 解释器带入了浏览器。Clang-Repl 在 WebAssembly 下也无法使用 LLVM 传统的 JIT 机制,因此我们引入了一种具有不同管道的 WebAssembly 执行模型: 1. 将生成的 LLVM IR 编译为 WebAssembly 目标文件。 2. 使用`wasm-ld`将该目标文件链接成一个 WebAssembly 侧模块——相当于 WebAssembly 中的动态加载共享库。 3. 将侧模块动态加载到运行中的应用程序中。 4. 解析其符号并调用编译后的函数。 每个新的输入都增量扩展了正在运行的程序。新加载的侧模块与主应用程序共享内存,其导出的符号可以被稍后加载的模块使用。 这项工作现在是浏览器中 Xeus-Cpp 的基础。这也是我们在FOSDEM 2026 上关于交互式 C++ 工作流演讲(https://fosdem.org/2026/schedule/event/QX3RPH-building_interactive_cc_workflows_in_jupyter_through_clang-repl/)的主题。 这个项目背后的核心认识是,类似的架构可以应用于 llvmlite。 我们实现了一个 WebAssembly 执行引擎,它接收通过 llvmlite 产生的 LLVM 模块,发出 WebAssembly 对象,通过其进程内可重入驱动程序调用 LLVM 的链接器 LLD,并将每个结果加载为 Emscripten 侧模块。在进程内使用 LLD 至关重要:在浏览器内部无法生成一个`wasm-ld`子进程。 这些模块被全局加载并保持存活,允许运行时库、编译器生成的辅助函数和用户函数相互解析。这为 llvmlite 提供了 Numba 所需的增量执行行为,同时尊重浏览器的安全模型。 在继续向更高层级推进之前,我们直接使用这个引擎来编译、优化、检查和执行 LLVM IR。我们还启用了进程内 Graphviz 渲染,使得可以显示控制流图,而无需将`dot`可执行文件作为子进程启动。 您可以在此探索这个更低级别的管道:JupyterLite 中的 llvmlite 和 Graphviz(https://notebook.link/@anutosh491/llvmlite)。 在 JupyterLite 中渲染的 LLVM 控制流图 *完全在浏览器中构建 LLVM IR 并渲染其控制流图。* ## 向上推进到 Numba(https://notebook.link/blog/numba-in-the-browser/#walking-up-the-stack-to-numba) Numba 从一个友好得多的层面开始。用户编写一个普通的 Python 函数并应用`@jit`或`@njit`: ``` from numba import njit @njit def add(a, b): return a + b add(1.0, 2.5) ``` 在这个小例子背后是一个完整的编译器管道。Numba 读取 Python 字节码并构建自己的中间表示。它为参数和中间值推断具体类型,执行编译器转换,并通过 llvmlite 将类型化程序降级为 LLVM IR。 在原生机器上,llvmlite 会将结果交给 LLVM 的 JIT 执行引擎。在 JupyterLite 中,它改而将模块交给新的 WebAssembly 引擎。该模块被编译、链接为侧模块,加载到运行中的 Xeus-Python 内核中,并通过 WebAssembly 函数表暴露出来,以便 Numba 可以调用它。 一个单独的用户函数可能涉及多个模块。Numba 可能首先加载运行时支持,如 Numba 运行时(NRT),然后加载编译器生成的辅助函数,最后加载包含用户函数及其 CPython 可调用包装器的模块。这些模块必须按正确顺序加载,以便后面的代码可以解析前面模块提供的符号。 我们还通过`@njit(cache=True)`添加了持久对象缓存。当启用缓存的函数在稍后的浏览器会话中再次使用时,Numba 从 JupyterLite 的持久文件系统中恢复其缓存的编译数据。llvmlite 重用相应的 WebAssembly 对象,同时将新的侧模块重新链接并加载到新的内核进程中。 结果是浏览器中真正的 Numba 编译和执行——不是模仿 Numba 的解释器,也不是隐藏在 notebook 后面的远程服务。 ## 在 emscripten-forge 上打包 Numba(https://notebook.link/blog/numba-in-the-browser/#packaging-numba-on-emscripten-forge) 一旦我们让 Numba 在浏览器中工作,采用的关键就是能够分发该软件。这正是emscripten-forge(https://emscripten-forge.org/)的作用。Emscripten-forge 是面向浏览器中 WebAssembly 的软件发行版。它建立在 conda/mamba 技术栈和 conda-forge 之上,为 Web 浏览器提供完整的包管理解决方案,并提供了广泛的包集合,包括 Python 科学栈(NumPy、SciPy、LLVM、Clang、LLD)、R 栈,以及原生命令行应用程序和完整的工具链。 Numba 现在可以在 emscripten-forge 中使用,并且可以轻松地在 JupyterLite 部署中使用! 在 JupyterLite 中使用 Mamba 安装 Numba *在 JupyterLite 终端中使用 Mamba 动态安装 Numba 的 WebAssembly 构建。* ## 解锁 Numba 之上的生态系统(https://notebook.link/blog/numba-in-the-browser/#unlocking-the-ecosystem-above-numba) 让标量加法编译成功是一个重要的里程碑,但更广泛的动机是一旦 Numba 可用,整个生态系统就会变得可能。 我们的演示从可识别的 Numba 示例和数值数组内核开始。然后它向上推进到直接使用 Numba 或通过自己的编译后端使用 Numba 的包: - **PyTensor**(https://pytensor.readthedocs.io/en/latest/)可以使用其 Numba 链接器编译符号数值图。 - **PyMC**(https://www.pymc.io/welcome.html),构建在 PyTensor 之上,将熟悉的概率模型带入相同的浏览器环境。 - **interpolation.py**(https://github.com/EconForge/interpolation.py)提供用于数值经济学的 Numba 加速插值例程。 - **Dolo.py**(http://econforge.org/dolo.py)在此栈之上为经济学家提供支持 JIT 的数值例程(随机过程、决策规则),以及针对动态规划问题的优化求解方法。 换句话说,启用一个编译器解锁的远不止一个包。它为统计、概率、经济学和科学计算库开辟了一条道路,这些库以前无法将其完整的执行模型带到 JupyterLite 中。 在 JupyterLite 中运行的 Numba 生态系统 *Numba 生态系统通过 Xeus-Python 在 JupyterLite 中运行。* 在此试用完整的进阶流程:浏览器中的 Numba、PyTensor、PyMC、Interpolation.py 和 Dolo.py(https://notebook.link/@anutosh491/numba-ecosystem)。 ## 接下来是什么?(https://notebook.link/blog/numba-in-the-browser/#what-comes-next) 当前的工作建立了端到端的架构,但还有更多事情要做。我们希望以聚焦的贡献将改动上游化,扩大 Numba 和 llvmlite 测试套件的覆盖率,改进性能和持久缓存,并验证更广泛的 Numba 生态系统中更多的包。 诸如 SIMD 之类的 WebAssembly 目标特性也为数值内核提供了一个令人兴奋的方向。在生态系统层面,依赖 Numba 的项目现在可以评估浏览器支持,而不是从一开始就排除 WebAssembly。 最重要的是,这项工作表明,一个复杂的编译器栈不必止步于 Web 浏览器的边界。通过 emscripten-forge 获得 LLVM、LLD、llvmlite 和 Numba,JupyterLite 可以从一个轻量级 Python 环境成长为一个严肃的编译型科学计算平台。 我们期待看到社区在其之上构建什么! ## 关于作者(https://notebook.link/blog/numba-in-the-browser/#about-the-author) Anutosh Bhat 是 QuantStack 的一名科学软件开发者。他是 LLVM 维护者,也是用于 C++ 的 Xeus-Cpp Jupyter 内核的合著者。Anutosh 领导了 llvmlite 和 Numba 的 WebAssembly 集成工作。 ## 致谢(https://notebook.link/blog/numba-in-the-browser/#acknowledgements) 这项工作是在 QuantStack 开发的,并建立在 LLVM、Clang-Repl、Emscripten、llvmlite、Numba、Xeus-Python、JupyterLite、Graphviz 和 emscripten-forge 的贡献之上。 我们感谢这些项目的维护者和贡献者,特别是感谢 Numba 社区围绕这项工作上游化的讨论。

相似文章