Python 3.13自由线程速度提升探索:能用于 CPU 密集型任务吗?
随着 Python 3.13 的发布,我想看看 Python 即将发生的最大变化,我认为到目前为止最令人兴奋的功能是来自 PEP-703 的自由线程 Python,因为它让Python抛弃GIL成为可能,大大提升了python性能。
在 Python 3.13 之前,由于 GIL 的原因,线程用于 IO 绑定任务,Asyncio 也用于 IO,我们可以使用 asyncio.to_thread
包装线程。例如:
await asyncio.to_thread(io_bound_task, "first_arg", optional="optional")
但我们可以将它用于 CPU 密集型任务吗?以下是直接从 Asyncio 文档中提取的引述:
注意 由于 GIL 的原因,asyncio.to_thread() 通常只能用于使 IO 绑定的函数成为非阻塞函数,但是,对于发布 GIL 的扩展模块或没有 GIL 的替代 Python 实现,asyncio.to_thread() 也可用于 CPU 绑定的函数,唯一阻止我们的是 GIL,因此 CPU 密集型任务应该不是问题,但是 AsyncIO 这个名字仍然让人感觉有点傻。
以下是使用 AsyncIO 测试脚本:
import argparse
import os
import sys
import time
from asyncio import get_running_loop, run, to_thread, TaskGroup
from concurrent.futures import ThreadPoolExecutor
from contextlib import contextmanager
def timer():
start = time.time()
yield
print(f"Elapsed time: {time.time() - start}")
def cpu_bound_task(n):
"""A CPU-bound task that computes the sum of squares up to n."""
return sum(i * i for i in range(n))
async def main():
parser = argparse.ArgumentParser(description="Run a CPU-bound task with threads")
parser.add_argument("--threads", type=int, default=4, help="Number of threads")
parser.add_argument("--tasks", type=int, default=10, help="Number of tasks")
parser.add_argument(
"--size", type=int, default=5000000, help="Task size (n for sum of squares)"
)
args = parser.parse_args()
get_running_loop().set_default_executor(ThreadPoolExecutor(max_workers=args.threads))
with timer():
async with TaskGroup() as tg:
for _ in range(args.tasks):
tg.create_task(to_thread(cpu_bound_task, args.size))
if __name__ == "__main__":
print("Parallel with Asyncio")
print(f"GIL {sys._is_gil_enabled()}") # type: ignore
run(main())
我在 M3 Macbook Pro 上运行了有和没有 GIL的对比:
不带GIL(python3.13版本)
python parallel_asyncio.py
Parallel with Asyncio
GIL False
Elapsed time: 0.5552260875701904
带GIL运行:
python parallel_asyncio.py
Parallel with Asyncio
GIL True
Elapsed time: 1.6787209510803223
结果正如预期的那样,当我们使用 AsyncIO 并发运行代码时,我们观察到了我们期望的结果,速度上的确进行了提升。
THE END