V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
gosky
V2EX  ›  Python

Python asyncio 中怎么执行 cpu 密集型任务?

  •  
  •   gosky · 2024-07-23 12:57:31 +08:00 · 3665 次点击
    这是一个创建于 408 天前的主题,其中的信息可能已经有所发展或是发生改变。
    理论上讲,两个办法:
    一、多线程,但不知道怎么全局解释锁被优化得怎么样
    二、多进程,消耗更多内存,因为需要加载模型,内存复制必须要考虑。多进程管理是个麻烦,启动耗时也得考虑

    期望有个轻量级的开源程序,轻松完成这个活……
    第 1 条附言  ·  2024-07-23 14:54:15 +08:00
    其中的 cpu 密集型,依赖 python 库
    第 2 条附言  ·  2024-07-24 08:54:54 +08:00
    问了下 ai ,pytorch c 扩展大部分会释放 gil ,于是决定采用多线程的方式
    但……实际运行后,发现虽然自己撸灵活,还是专用推理服务更节省资源
    25 条回复    2024-08-02 10:49:58 +08:00
    changz
        1
    changz  
       2024-07-23 13:04:13 +08:00 via Android
    多线程+c/c++ ffi
    gaogang
        2
    gaogang  
       2024-07-23 13:20:20 +08:00
    cpu 密集的任务 用其他合适的语言实现
    然后通过 rpc 暴露出来 正好给 python 的 asyncio 调
    lambdaq
        3
    lambdaq  
       2024-07-23 13:23:53 +08:00   ❤️ 1
    asyncio 顾名思义是 异步 io 。。。众所周知,py 要么卡 cpu 要么卡 io ,asyncio 只解决卡 io 的问题啊。
    ysn2233
        5
    ysn2233  
       2024-07-23 13:32:10 +08:00   ❤️ 1
    用 c/c++/rust 的 ffi 做 binding 吧,实在不行 java/go 什么的也行,python 就做个前端调一下
    mirrornighth
        6
    mirrornighth  
       2024-07-23 13:32:51 +08:00
    多进程
    ipwx
        7
    ipwx  
       2024-07-23 13:34:03 +08:00
    “因为需要加载模型,内存复制必须要考虑。”

    你是啥模型。有些模型开多线程是可以并行的。
    Goooooos
        8
    Goooooos  
       2024-07-23 15:05:21 +08:00
    python 3.13 可以加个参数去掉 GIL
    guochao
        9
    guochao  
       2024-07-23 15:24:16 +08:00
    针对锁和 python 效率的问题:
    - 如果你的计算任务可以用 c/rust 的模块,最好是在 c/rust 的部分处理,最好能批处理
    - 如果是 pure python ,也许可以试试 taichi 或者类似的东西。因为 python 可以被再次编译成 native 或者 gpu kernel ,并针对架构优化
    针对 asyncio 只管异步切换,计算存在长时间阻塞的问题
    - (不同进程、单独服务的方案)要么 celery ,或者自己搓一个简单的队列
    - (简单在同一个进程中的方案)要么 thread pool executor
    itskingname
        10
    itskingname  
       2024-07-23 16:04:22 +08:00
    chenqh
        11
    chenqh  
       2024-07-23 16:17:48 +08:00
    你加载的模型很大吗?不然想不出来为什么不用多进程
    ruanimal
        12
    ruanimal  
       2024-07-23 16:43:54 +08:00
    跑模型还是多进程好
    razertory
        13
    razertory  
       2024-07-23 16:45:46 +08:00
    asyncio 是做 IO 密集任务的,不是做 CPU 密集任务
    scipelaina
        14
    scipelaina  
       2024-07-23 20:07:56 +08:00
    `asyncio.to_thread`; `concurrent.futures`.
    日经问题,Mark as Duplicated.
    wujian752
        15
    wujian752  
       2024-07-23 22:48:26 +08:00 via iPhone
    如果是神经网络模型现在 onnxruntime 在 inference 的时候可以不受 GIL 影响
    crackidz
        16
    crackidz  
       2024-07-23 22:57:16 +08:00
    Python 主要冷启动耗时几十 ms 左右,剩下的交到你的代码中了,启动时间主要取决于你的代码吧...

    上面说的基本差不多了,但现在不推荐用那个 nogil 版本,要改你的代码支持部分功能,第三方库也未见得支持。另外还有一些隐蔽 bug...
    yoiteshaw
        17
    yoiteshaw  
       2024-07-23 23:04:14 +08:00   ❤️ 1
    我工作中遇到过类似的事情,两年前,无论怎么优化都收效甚微,多线程因为 GIL 反而更慢,最后不得已用多进程去做,用 socket 去通信。
    今年我推翻用 rust 重写了,一些逻辑直接的遍历,简简单单 pair_iter 解决,心情舒畅。
    sagaxu
        18
    sagaxu  
       2024-07-23 23:28:18 +08:00
    Python 的 CPU 密集型库大都用 C 实现,大部分时间不会持有 GIL
    skuuhui
        19
    skuuhui  
       2024-07-24 08:42:30 +08:00
    最简单是写个 c++模块,用 python 去调。
    其他方法有
    1. 换个没锁的解释器
    2. wasm
    3. https://numba.pydata.org/https://www.trypyjion.com/
    Nich0la5
        20
    Nich0la5  
       2024-07-24 09:19:55 +08:00
    asyncio 就不是给你做 cpu 密集型用的
    gray0
        21
    gray0  
       2024-07-24 09:21:41 +08:00
    何必多 BB 直接上代码

    from concurrent.futures import ProcessPoolExecutor
    import asyncio

    async def integration_process_and_gather():
    with ProcessPoolExecutor() as process_pool:
    loop = asyncio.get_running_loop()
    numbers = [100000000, 1, 100, 10000, 1000000, 1000, 100000]
    tasks = [
    loop.run_in_executor(process_pool, do_count_number, n) for n in numbers
    ]
    [print(type(task)) for task in tasks]
    results = await asyncio.gather(*tasks)
    print(f"integration_hello_world {results}")
    yh7gdiaYW
        22
    yh7gdiaYW  
       2024-07-24 11:33:34 +08:00
    简单点就多进程,复杂点就上 ray ,可以分布式计算用得很爽
    lttzzlll
        23
    lttzzlll  
       2024-07-25 17:38:07 +08:00
    你提出的问题,限制你的思路。web 服务要简单,轻量,快速响应。推理服务耗时耗资源。正确的方式应该是 同一个项目(git repo),部署多个进程。经典的做法应该是 一个 web 服务进程,多个 worker 进程,web 服务和 worker 进程之间用队列。以 django/flask 举例:
    lttzzlll
        24
    lttzzlll  
       2024-07-25 17:53:54 +08:00
    @lttzzlll 。。。你面的问题不是“Python asyncio 中怎么执行 cpu 密集型任务?”。 换成其他的 web 框架或语言,就没有这种问题了吗?而是在 web 服务中,如何处理比较耗时/耗资源的任务。这些问题都有很经典很成熟的方案。把这类任务放到 worker 节点上,用队列传递消息,不够就增加 replica 的数量。非常成熟和广泛使用的方案,你的场景也不例外。
    cooljiang
        25
    cooljiang  
       2024-08-02 10:49:58 +08:00
    CPU 密集型任务建议换个没 GIL 的 Python 解释器,如 PyPy 之类的。
    关于 GIL: https://mp.weixin.qq.com/s/lIkcTuCX5htQcteklCFaZw
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1371 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 17:12 · PVG 01:12 · LAX 10:12 · JFK 13:12
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.