使用 PyScript 实现无后端

今年是 Anaconda 成立 10 周年,我们一直在花时间反思我们作为一家公司以及作为 Python 开源开发社区的一份子所取得的成就,并思考我们未来的发展方向。 还有很多事情要做,在这篇博文中,我将尝试概述我们看到的 Python 的一些新挑战,以及 Anaconda 的开源开发工作的发展方向。
在深入研究像这样的内容广泛的愿景文件之前,重要的是要承认其视角的局限性。我想谈谈 Anaconda 开源软件 (OSS) 开发者正在关注的问题,但这并不是宣布这些问题是 Python 生态系统中每个人都应该关注的问题。 当存在多样化的视角、优先级和技能,异步运行以探索可能的软件空间时,OSS 才能发挥最佳作用。 我们中只需要一个人发现一个好主意,编写一段有用的代码,或撰写一份优秀的文档,我们所有人都能从中受益。 目标应该是找到一个我们每个人的视角和技能组合都能产生积极影响的地方,而这篇博文反映了我们认为 Anaconda 非常适合帮助推动开源 Python 前进的方向。
在谈论我们希望在 Python 的未来 10 年中看到什么之前,我首先想停下来讨论为什么我们希望 Python 继续成为一种流行的语言。 Python 之所以能够在广泛的编程用例中占据主导地位,原因有很多:
对于非传统程序员来说,它足够容易学习。
对于专家来说,它足够强大和通用,可以创建复杂的软件。
对于具有不同软件开发专业水平的人员,存在从 Python 用户到 Python 生态系统贡献者的路径。
它很容易在所有主要的桌面操作系统上使用,并且在许多平台上具有相当的可移植性。
广泛领域的领域专家为自己和同事构建专门的软件包。
Python 很容易与其他软件接口,无论是加载来自其他语言的编译库、通过网络与服务器通信,还是使用优秀的传统命令行脚本进行系统编排。
当然,Python 在某些方面未能达到这些理想,但它在满足这些理想方面已经足够成功,成为世界上最顶级的语言之一。 我们希望利用这些优势,并继续推动 Python 成为任何人都可以随时随地使用的计算工具——这是一个挑战,因为如果没有探索一些新的方向,Python 将会错过“任何人”和“任何地方”的很大一部分。
随着我们面临越来越多的计算选择,将 Python 变成“任何人都可以随时随地使用的东西”的目标变得越来越难以实现。 在 Python 存在的大部分时间里,绝大多数开发者只需要担心少数几家芯片供应商和操作系统,新平台的出现速度缓慢,更奇特的系统架构通常只对少数专家开发者和用户可用。 许多技术趋势已经永久打破了这种现状,虽然许多 Python 开发者一直在努力使 Python 适应新的现实,但仍有许多工作要做。
在微型计算机的大部分历史中,我们可以依靠硬件性能逐年提高,而无需对我们编写的软件进行任何根本性的更改。 这种情况在 2000 年代中期停止了,因为多核中央处理器 (CPU) 从稀有变得基本上是强制性的,以便继续提高通用 CPU 的性能。 从那时起,我们看到了芯片专业化的兴起,以从固定的功率预算中挤出更多性能,包括矢量指令集、图形处理单元 (GPU)、AI 加速器、片上硬件加速视频编解码器以及大量硅初创公司试图将其他新颖的想法推向市场。
从某种意义上说,Python 作为一种高级解释型语言,非常适合编写可以在许多不同芯片架构上运行的代码,只要有人完成了移植 Python 解释器并重新编译所有需要的 Python 扩展模块的繁重工作。 然而,同一个 Python 解释器内置了许多假设,这使得多核 CPU 的使用具有挑战性,并且没有直接为利用可用的众多专用加速器技术(如 GPU)提供任何帮助。 这项工作落在了蓬勃发展的 Python 模块生态系统上,但每个模块基本上都必须从头开始构建自己的编译器基础设施来支持这些新的计算形式。 对于大多数较小的 Python 项目来说,添加加速计算支持仍然非常令人生畏。
对于我们大多数人来说,我们编程的东西是一台“计算机”,我们将其想象为运行在 CPU 上的操作系统,该 CPU 可以访问三个位置之一的数据:RAM、“磁盘”(SSD 或其他)和网络。 RAM 比磁盘快得多,磁盘同样比网络快得多。 对于计算密集型工作负载,我们通常忽略磁盘和网络的速度作为一次性启动成本,并将 RAM 想象为统一快速的数据源,而不管它在内存中的布局方式或哪个 CPU 核心正在访问它。 数据要么“在内存中”,要么不在内存中,我们不需要担心除此之外的任何事情。
实际上,计算机架构更加多样化和复杂。 我们拥有具有大量 CPU 核心的服务器,这些核心对内存的访问不均匀,可能存在多级具有不同速度和延迟特性的非易失性内存,具有自己的内存系统和互连策略的加速器(如 GPU),以及极快和低延迟的网络技术。 计算机内部看起来更像一个集群,资源通过令人眼花缭乱的总线技术阵列连接。 另一方面,高性能网络和集群编排工具正在使机架或数据中心中服务器之间的边界变得更加模糊。 因此,拥有 Python 工具(例如 Dask)至关重要,这些工具允许用户轻松地跨多个计算域划分计算,管理数据局部性,并在可能的情况下将计算移动到数据。
早在 Anaconda 的早期(当我们还被称为 Continuum Analytics 时),我与企业客户讨论的常见问题之一是他们何时以及是否会将他们的一些工作负载迁移到云计算服务。 这些时间表通常在遥远的未来,但现在过渡时期大多已经过去。 云计算现在已成为每个组织 IT 战略的一部分(即使只是混合方法的一部分),云技术也逐渐渗透到许多个人开发者和研究科学家的工具箱中(例如,请参阅 Pangeo)。
这种趋势并没有给 Python 本身带来许多新的挑战,因为大多数云 API 已经与优秀的 Python 库配对。 相反,云计算降低了使用趋势 #1 和 #2 中所有新技术的资本成本。 在几分钟内,任何拥有信用卡的人都可以访问具有最先进的 CPU、内存和网络技术的服务器或整个集群。 阻止此类用户从这种惊人的硬件中获益的唯一因素将是他们的软件堆栈的灵活性以及他们将代码移植到实际利用它的难易程度——或者缺乏灵活性和难易程度。
当 Anaconda 发行版首次创建时,最难解决的问题是在 Windows、Mac 和 Linux 上构建和运行最流行的 Python 数据科学软件包。 虽然在各个方面都不兼容,但这三个桌面操作系统比它们的不同之处更相似。 Python 非常适应 UNIX 风格的命令行、文件、套接字、进程和线程世界,这些都在每个流行的桌面操作系统中以某种形式存在。 在某些注意事项下,Python 用户可以在这些平台中的任何一个平台上进行开发时感到非常自在。
然而,在过去的 10 年中,计算领域发生了巨大的转变。 拥有“个人电脑”的人比以往任何时候都多,但它们不是 Python 成长过程中使用的传统“PC”。 最常见的个人计算系统现在是手机、平板电脑和 Chromebook。 即使在桌面系统上,大多数用户也花费大量时间使用网络浏览器。 因此,2022 年最流行的软件平台是 (1) Web 和 (2) 移动操作系统,如 iOS 和 Android。 这些新平台的突出特点是它们与旧的桌面操作系统截然不同。 文件和网络访问存在重大的安全限制,默认情况下它们没有内置的命令行界面,并且软件分发机制对于习惯于编写 Python 脚本的人来说是完全陌生的。 然而,网络浏览器和移动设备是下一代 Python 用户需要学习如何编码和分享他们工作的地方。 我们如何为他们提供这样做的工具?
我们无法通过一个神奇的解决方案来应对这些挑战,而是需要几个重叠、互补的战略方向,这些方向将相互支持。 我们在 Anaconda 确定了五个我们认为我们有能力产生影响的领域。 这些领域中的每一个都比任何单个开源项目都大,但我将在过程中提到一些示例项目。 该列表不会是全面的,也不应暗示我们认为其他项目也没有解决这些问题。 我们将需要来自许多不同群体的各种方法来应对我们面前的挑战。
将人类可读程序映射到硬件可执行机器代码的关键工具是编译器。 我们可能会认为 Python 作为一种最常见实现中的解释型语言,不需要担心编译器,但我们已经看到证据表明编译器技术对于使 Python 提高性能至关重要。 但 Python 不需要单一的通用编译器; 它需要一系列编译器工具来满足不同的需求。
在最广泛的范围内,我们希望看到 Python 解释器可以结合即时编译方法,以消除执行动态类型语言的一些开销。 正如许多人指出的那样,Python 的设计使这非常具有挑战性,但对于绝大多数 Python 用户来说,我们可以做更多的事情。 微软正在进行的 Faster CPython 工作,以及 Pyston 和 PyPy 等项目,都在使用各种编译器技术来尝试加速每个 Python 程序。 在这方面可以做更多的工作,不仅限于单线程性能。 我们还认为 CPython 的 nogil 分支非常有前景,并希望看到其中的想法融入到 Python 解释器中。
除了改进 Python 解释器的广泛方法之外,还有针对特定领域或独特硬件目标的更专注的编译器的空间。例如,Anaconda 与 PyData 社区合作开发 Numba 已有十年之久,并且已被广泛的科学和数值计算用户所采用。Numba 专注于数值计算,主要是使用 NumPy 数组,这使其在用户代码上实现了极高的加速(从 2 倍到 200 倍不等)。我们已经看到了 Numba 有限的范围和可扩展性使其能够移植到非 NumPy 数据结构以及新的硬件目标的各种方式。我们认为这个想法可以进一步推进,以激发创建一套模块化组件,用于提前编译以及即时编译(和混合!)编译器用例。这将进一步扩大编译器方法在生态系统中的采用,并使 Numba 成为 Python 项目中使用的一大类编译器的一个例子。
请注意,与编译器讨论相邻且非常相关的是分发编译后的软件。我们仍然相信 conda 在这方面将极其重要,因为我们需要能够向越来越多的平台交付各种编译后的库和应用程序(不仅仅是 Python)。其中一些平台将非常不寻常,例如 Web 浏览器和嵌入式系统,因此 conda 的功能需要继续增长。
像 Numba 和 Dask 这样的项目之所以成功,一个被低估的方面是它们的创建依赖于 NumPy 和 pandas 等项目在此之前被广泛采用。这些项目为 Python 用户提供了一个通用的词汇表和心智模型,用于批量处理数据,形式为多维数组 (NumPy) 或数据帧 (pandas)。在 Dask 的案例中,这些高级 API 通常非常适合并行计算,并且可以直接移植到 Dask 的容器 API。这意味着 NumPy 或 pandas 用户可以切换到 Dask,在数十个节点上并行化他们的计算,而实际上不必学习许多新概念。在 Numba 的案例中,NumPy 使用的好处更低级。由于 NumPy 数组具有良好理解且简单的内存布局,因此像 Numba 这样的工具可以动态生成高效的机器代码来处理 NumPy 数组,这对于存储为 Python 对象集合的数据来说是不可能的。如果没有广泛使用 NumPy 数组作为操作数值数据的标准方式,Numba 将很难在生态系统中获得采用。
数组和数据帧一直是过去十年的焦点,但还有其他类型的数据。我们最近更多地参与到推广 Python 生态系统中其他数据模型的项目中,例如 Awkward Array 项目。Awkward Array 来自高能物理社区,该社区需要处理大型半结构化数据集,这些数据集具有可变大小的嵌套列表和缺失数据。这种多样化的数据以 NumPy 数组或 pandas 数据帧的形式处理起来非常笨拙(因此得名 Awkward),但在各种应用中都能看到。同样,我们也在努力改进稀疏矩阵的采用,特别是来自 GraphBLAS 的扩展定义。GraphBLAS 试图使用线性代数的语言创建图算法的标准构建块。我们认为像 GraphBLAS 这样的工具在 Python 生态系统中具有很大的潜力,它们的使用将使未来在稀疏和不规则形状数据上并行化和扩展计算的工作变得更加容易。
感谢像 Dask(以及其他几个项目)这样的项目,Python 拥有一套强大的工具,用于在计算机集群上处理大型数据集。这些工具的本质是将数据集划分为独立的块,最大限度地减少数据移动,并在可能的情况下将计算带到数据端。这使得在一个通信延迟和带宽是重要约束的世界中能够实现高效扩展。与此同时,这些工具为集群提供了一个非常有用的抽象层,允许用户像编程单个系统一样对其进行编程,将调度程序留给决定如何以及在何处执行代码。尽可能简化分布式计算范例将继续是 Anaconda 的一个持续目标。
同样有价值的是将编程单台计算机视为编程集群。在硬件层面,这在某种程度上已经是事实,因为系统可以由多个 CPU 核心组成,这些核心具有“首选”内存区域,访问速度更快。此外,计算可能发生在服务器内的加速器设备(如 GPU),它们拥有与 CPU 不同的内存。然而,Python 特有的最大问题是,Python 中的全局解释器锁限制了即使是编译后的扩展程序也无法在一个 Python 进程中充分利用高核心数系统的能力。从长远来看,我们需要解决像全局解释器锁 (GIL)(但不仅限于 GIL!)这样阻碍 Python 在多线程工作负载中表现的问题。与此同时,像 Dask 这样的工具提供了一个很好的临时解决方案,即在单个系统上启动一组 Python 进程并在它们之间划分工作。Dask 的轻量级架构非常适合在单台计算机上使用,但我们可以玩一些额外的技巧,使“单系统集群”用例更高效。鉴于云端大型服务器的可用性,我们认为提高 Python 在高核心数服务器上的效率具有很大的潜在优势。
之前的大部分讨论都集中在计算上:如何描述计算,如何加速计算,以及如何在多台计算机之间划分计算。同样重要但较少被提及的是方程的 I/O 侧。你的数据在哪里?你如何轻松高效地加载它?
最明显的答案是将所有数据放在一个地方,并使用少量的文件格式。这种标准化是一个有吸引力的选择,一些组织可以花费必要的资源来创建和强制执行数据湖的一致使用。然而,许多组织(和大多数个人)没有能力完全强制执行数据湖,这使得数据工作者不得不处理跨多个不同系统和文件格式的数据源的扩散。事实上,对于某些团队来说,快速将新的数据源集成到分析中,而无需等待适当的数据湖集成,可能是一种竞争优势。
正如我们押注计算异构性一样,我们也押注数据异构性。数据将存在于它存在的地方,虽然集中和组织数据对许多团队来说是一个有价值的目标,但这项任务永远不会完成,工具需要承认这一现实。为了实现这一目标,我们致力于许多旨在让 Python 用户轻松访问最广泛的数据存储系统和文件格式的项目。Fsspec 已成为许多项目(包括 Dask)流行的类似文件系统的抽象层,它允许将本地存储、云端对象存储(如 S3)或各种服务器 API(如 Google Drive)中存储的文件以相同的方式处理。在这些抽象之上,我们创建了 Intake,它提供了一个灵活的插件架构和一个基于 YAML 的轻量级规范,用于数据源目录。Intake 的设计目标是在 NumPy 和 pandas 等工具以及用于更大规模分布式工作负载的 Dask 中同样出色地工作。
我们最近添加到数据访问领域的是一个名为 Kerchunk 的项目,它允许索引通常不是为高效云计算设计的存档数据格式。Kerchunk 可以扫描各种格式(如 NetCDF、TIFF 等)的潜在大型文件集,并构建数据的元数据索引,以使 Xarray 等现代工具能够更高效地访问这些数据,而无需首先转码数据。能够使所有数据,甚至更旧的数据,在云环境中易于使用是我们 Python 策略的关键部分。
10 年前,许多 PyData 开发人员倾向于忽略他们个人不使用的平台,通常是 Windows。资源稀缺,许多维护者是志愿者,那么他们为什么要关注一个他们不熟悉且难以测试的平台呢?这并非不合理的态度,但如果不是少数热心人士(向 Christoph Golke 等人致敬)的努力,他们保持了 PyData 在 Windows 上的支持,那么这将把大量的用户排除在 Python 生态系统之外。当我在 Anaconda 展位工作时,Anaconda 的 Windows 用户是我在会议上遇到的一些最感激的用户。最终,提供 Windows、macOS 和 Linux 支持的免费持续集成服务的出现,使得所有三个主要的桌面操作系统在 PyData 生态系统中几乎成为平等的公民。
我相信我们正处于再次犯下 Windows 规模错误的边缘,即将 Web 浏览器和移动操作系统视为“太奇怪”而不能成为一流的 Python 平台。当然,这些平台的局限性比 Windows 和 Linux 之间的差异要大得多。然而,对于数量庞大的用户来说,这些是主要的计算平台。我们需要投入精力,弄清楚 Python 在这些平台上应该是什么样子,否则就有可能让整整一代计算机用户错过 Python 生态系统所能提供的伟大之处。
现在,我们不必从头开始。由于像 Jupyter 这样的“notebook”项目(以及像 Sage 这样有影响力的旧项目),Python 在浏览器中已经可见多年。Jupyter 提供了一个基于浏览器的前端,用于在内核(通常是 Python)中运行代码,该内核在浏览器外部执行。Python 内核作为某个地方的普通应用程序运行,可以在用户的计算机上,也可以在远程服务器上,并通过 websocket 与基于 Web 的前端通信。这种架构巧妙地规避了在 Web 浏览器内部执行代码的所有限制,但也意味着 Jupyter notebook 不是真正独立的。我可以查看 notebook,但除非我在某个地方创建一个 Python 环境并启动 Jupyter 来加载 notebook,否则我无法与之交互。
我们连接 Python 和浏览器的长期工作还包括我们帮助创建和推广的数据可视化项目堆栈,例如 Bokeh、Datashader、HoloViz 和 Panel。这些项目都旨在使 Python 开发人员能够创建在 Web 浏览器中实时、交互式的数据可视化,而无需成为全新的前端技术堆栈的专家。我们已经看到许多用户在被赋予从 Python 定位浏览器的能力时创造了令人惊叹的东西。
但是,如果我们可以更进一步,将 Python 运行时本身推送到浏览器中呢?事实上,JupyterLite 已经在为 notebook 做这件事了!更广泛地思考,如果我们将 Python 视为浏览器内的一流语言,并利用我们已经拥有的广泛的 Python 包库,我们可以构建很多伟大的东西。沿着这些思路,我们对 WebAssembly 将许多语言社区(不仅仅是 Python)带入浏览器的潜力非常感兴趣。Python 已经是桌面端许多语言和库之间的通用粘合剂。将其放入浏览器(通过 Pyodide 和 PyScript 等项目)有可能将 Python 世界中最好的包与 Javascript 粘合在一起。这项工作将充满挑战,并且许多事情在浏览器安全模型的限制内根本不可能实现,但即使在浏览器中支持 Python 可以做的部分或大部分事情,也将使许多伟大的应用程序得以构建。
对于移动操作系统,情况相对简单一些。iOS 和 Android 都比 Web 浏览器更接近 macOS/Linux,Python 可以在进行更少修改的情况下运行。这里的大部分挑战都与为本机接口(GUI 和其他平台服务)提供 Python 接口以及自动化为各种应用商店打包应用程序的陌生过程有关。这就是像 BeeWare 这样的项目非常令人兴奋的地方,其子项目正在解决每个问题:Toga (GUI)、Rubicon (本机 API 桥接) 和 Briefcase (应用程序打包)。可以烘焙到 BeeWare 中的平台知识越多,任何技能水平的 Python 开发人员将其原型转化为应用程序并与更广泛的受众共享就越容易。
简而言之,是的!PyData 生态系统在过去十年中的爆炸性增长为今天的 Python 用户带来了在 2012 年难以想象的功能和特性。浏览器中的交互式数据可视化、Python 中的 GPU 加速计算以及 Jupyter notebook 中轻松的分布式计算在当时似乎都是遥远的目标,但现在它们已成为现实。想象一下,在未来十年,当我们拥有比那时更大的社区时,我们能做什么。
但是,我们将不得不共同努力。本文中描述的想法将是 Anaconda 开源工作的大部分重点,但如果只有我们这个小团队在为此工作,这些想法将无法发挥其潜力。我们希望其他人也会受到启发来解决这些领域,无论是加入我们的关键项目,还是自行尝试他们的想法。只要我们专注于各自的优势,继续扩大贡献者社区,并互相学习,伟大的事情就会发生!
注意:这篇文章最近经过改编并由 The New Stack 发布。