提问人:tkngoutham 提问时间:11/7/2023 最后编辑:jsbuenotkngoutham 更新时间:11/9/2023 访问量:45
在给定场景中应使用哪个 python 并发 API [已关闭]
Which python concurrency API should be used in given Scenario [closed]
问:
假设我们正在开发一个 python3 程序,该程序正在尝试计算一些朴素的整数分解,即一些计算密集型任务,以下哪些更改将有助于改进运行时?
线程以运行多个操作系统级别的线程来运行每个任务。这很有帮助,因为操作系统级别的同步速度更快。
AsyncIO 运行多个应用程序级线程来运行每个任务。这很有帮助,因为这减少了执行任何类型的内核上下文切换的需要,并且仍然利用多个 CPU 内核。
多个系统进程来运行每个任务。这很有帮助,因为所有 CPU 内核都得到了正确有效的使用。
我试图理解所有三个 API 之间的基本概念差异,但这是一个非常具体的问题。
我期待一个概念上支持的答案,这个问题解决了关键的拱级问题
答:
这实际上取决于您手头的工作量。
此外,从 Python 3.12 开始,还有一个级别需要考虑:Python 现在可以运行多个解释器,每个解释器都有一个独立的 GIL。
归根结底,如果你的“朴素因式分解”是纯 Python 代码,或者它是否利用了在本机代码中运行的任何数字库(即由 Python 运行时调用并自行运行的二进制代码,只是返回结果),例如 NumPY。
这很重要,因为此类库通常会在进入计算部分时释放 Python 的解释器锁 (GIL)。在这种情况下,Python 多线程很好:它将利用所有 CPU 内核,你也没问题。
在这种情况下,使用 asyncio 模块的纯异步通常不会解决问题:数值库通常会在输入计算之前释放 GIL - 但是为了在异步模型中使用协作多任务处理,它们必须使用回调方案,以便每个数值计算都是“可等待的”,以便控件可以返回到异步循环。对于纯 Python 计算代码,就像使用多线程代码一样,这简直是禁忌:asyncio 意味着一次运行单个 Python 线程。这意味着,即使没有 GIL 限制,Python-VM-Calculation 绑定代码也无法从 asyncio 中受益,并且将保持如此。(当然,可以将异步与其他三种方法中的任何一种结合起来,以驱动在工作线程中运行的任务)
至于多处理:启动每个工作线程会产生开销,传递参数和返回值会产生开销,并且,正如您所说,每次任务切换时都会产生额外的 CPU 开销 - 但尽管如此,这是可靠地使用多个 CPU 内核来执行任务的简单方法。如果计算使用 Python 代码,这可能是要走的路。
消息是,从 Python 3.12 开始,有可能在单个进程中启动多个 Python VM(“解释器”)。它们可以与多线程结合使用,以可靠的方式在多个 CPU 内核中同时运行纯 Python 代码。这是更快的任务切换:它使用操作系统线程,每个内核甚至可以在不切换的情况下连续运行计算 - 但传递参数仍然需要序列化,就像在多处理中一样。
目前还没有使用子解释器的公共 API。“秘密”在于,在 Python 3.12 中,您可以执行并使用 PEP 554 上提供的文档来评估一些引导代码,并在其他解释器上运行计算。import _xxsubinterpreters as interpreters
还有刚刚在 Alpha 中发布的 3rdy party 库,它允许人们这样做 - 它使尝试子解释器变得容易得多,您可以尝试一些基准测试 - 在另一个解释器(和线程)中启动一个函数,它就像这样简单: (仅限 Linux 或 intel Mac - 虽然,如果你能构建它, 它应该适用于 Windows 和 ARM Mac),然后:extrainterpreters
pip install extrainterpreters
import extrainterpreters as EI
def my_naive_factoring_function(*args):
...
interp = EI.Interpreter().start()
interp.run_in_thread(my_naive_factoring_function, *args)
# maybe start other interpreters
...
while not interp.done():
...
# sleep, or do stuff
# result retrieved in main interpreter:
result = interp.result()
(免责声明:我是“extrainterpreters”包的作者。但不是 Python 3.12 中 GIL 独立子解释器的功能 - 这要归功于 Eric Snow。
此外,额外解释器中有一个 Queue 类,随着时间的推移,它比继续轮询更容易,但它在这个 alpha 版本中并不可靠).done()
评论