在给定场景中应使用哪个 python 并发 API [已关闭]

Which python concurrency API should be used in given Scenario [closed]

提问人:tkngoutham 提问时间:11/7/2023 最后编辑:jsbuenotkngoutham 更新时间:11/9/2023 访问量:45

问:


想改进这个问题吗?更新问题,使其仅通过编辑这篇文章来关注一个问题。

5天前关闭。

假设我们正在开发一个 python3 程序,该程序正在尝试计算一些朴素的整数分解,即一些计算密集型任务,以下哪些更改将有助于改进运行时?

  1. 线程以运行多个操作系统级别的线程来运行每个任务。这很有帮助,因为操作系统级别的同步速度更快。

  2. AsyncIO 运行多个应用程序级线程来运行每个任务。这很有帮助,因为这减少了执行任何类型的内核上下文切换的需要,并且仍然利用多个 CPU 内核。

  3. 多个系统进程来运行每个任务。这很有帮助,因为所有 CPU 内核都得到了正确有效的使用。

我试图理解所有三个 API 之间的基本概念差异,但这是一个非常具体的问题。

我期待一个概念上支持的答案,这个问题解决了关键的拱级问题

python-asyncio python-multithreading python-subinterpreters

评论

0赞 tkngoutham 11/7/2023
if io_bound: if io_very_slow: print(“使用 Asyncio”) else: print(“使用线程”) else: print(“多处理”)
1赞 Paul Cornelius 11/8/2023
你的 #2 似乎包含一些有问题的假设。Asyncio 是一种单线程、协作式多任务范式。一些库函数可能会使用额外的线程,而 asyncio 有一些工具可以跨线程和进程协调活动。但这需要对项目 1、2 和 3 进行某种组合。计算密集型代码建议多处理,这是 Python 利用多个内核的最直接方法。但我不知道你把你的问题描述为“非常具体”是什么意思。

答:

0赞 jsbueno 11/9/2023 #1

这实际上取决于您手头的工作量。

此外,从 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),然后:extrainterpreterspip 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()