编程与软件工程calpaterson.com
异步 Python 并不会更快
Async Python is not faster
2020 — 2021,它一次次回到首页
黑点是上首页(≥ 50 分)的那次投稿,空心点是没冲上去的投稿,点越大分数越高。
导读
作者通过基准测试对比了 Python 中异步框架(asyncio/Sanic/Starlette)与传统同步框架(Flask 等)在数据库查询场景下的性能,结论是异步并不更快,甚至可能更慢。文章挑战了“async 等于高性能”的流行观念。
为什么经久不衰:它促使人们理性看待 Python 异步编程的真实收益与开销,这一争论至今仍在,是选择技术方案时值得参考的反思。
HN 评论精选
协程的目的是提高 I/O 等待期间的资源利用率,而非单次执行速度
这个结果有什么好惊讶的?协程的意义不是让代码跑得更快,而是避免进程在等待 I/O 时闲置。当外部 REST API 需要几秒才响应时,异步版本实际上“更快”,因为进程可以在等待时做其他有用的工作。异步框架当然有开销,但这点开销远小于等待外部服务时浪费的几十亿个 CPU 周期。
phodge查看英文原文
How is this result surprising? The point of coroutines isn't to make your code execute faster, it's to prevent your process sitting idle while it waits for I/O. When you're dealing with external REST APIs that take multiple seconds to respond, then the async version is substantially "faster" because your process can get some other useful work done while it's waiting. Obviously the async framework introduces some overhead, but that bit of overhead is probably a lot less than the 3 billion cpu cycles you'll waste waiting 1000ms for an external service.
基准测试方法有缺陷:异步连接池配置过小,测试场景不真实
他的异步代码只用了默认 10 个最大连接的连接池,而同步的 Flask 应用有 16 个 worker,数据库连接明显更多。我预计调大这个数字会改善 asyncio 的成绩,因为整个测试衡量的其实就是数据库连接数,离真实负载非常远。改成并行请求 httpbin 再写入数据库,才是真实的 asyncio 工作负载。
orf查看英文原文
His async code creates a pool with only 10 max connections[1] (the default). Whereas his sync pool[2], with a flask app that has 16 workers, has significantly more database connections. I expect upping this number would have a positive effect on asyncio numbers because the only thing[3] this[4] is[5] measuring[6] is how many database connections you have, and is about as far from a realistic workload as you can get. Change your app to make 3 parallel requests to httpbin, collect the responses and insert them into the database. That's an actually realistic asyncio workload rather than a single DB query on a very contested pool. I'd be very interested to see how sync frameworks fare with that. 1. https://github.com/calpaterson/python-web-perf/blob/master/async_db.py#L9 2. https://github.com/calpaterson/python-web-perf/blob/master/sync_db.py#L11 3. https://github.com/calpaterson/python-web-perf/blob/master/app_aio.py#L8 4. https://github.com/calpaterson/python-web-perf/blob/master/app_flask.py#L11 5. https://github.com/calpaterson/python-web-perf/blob/master/app_sanic.py#L11 6. https://github.com/calpaterson/python-web-perf/blob/master/app_starlette.py#L9
支持作者观点:不应盲目为“速度”而使用异步框架
我非常高兴终于有人研究这个问题。早就该终结那种仅凭“模糊的速度幻想”就条件反射式使用 asyncio 或 gevent/eventlet 的做法了。比如那些实际只服务单个用户的 Web 应用也用 Tornado 来图“速度”。我几年前就写过关于“异步等于速度”这一迷思的文章,结论完全相同。
zzzeek查看英文原文
I am SUPER happy someone else is finally looking at this. It is long past time that the reflexive use of asycnio or systems like gevent/eventlet for no other reason than "hand-wavy SPEED" come to an end. That web applications that literally serve just one user at at time are built in Tornado for "speed". (my example for this is the otherwise excellent SnakeViz: https://jiffyclub.github.io/snakeviz/ which IMO should have just used wsgiref). As the blog post apparently cites as well (woo!), I've written about the myth of "async == speed" some years ago here and my conclusions were identical. https://techspot.zzzeek.org/2015/02/15/asynchronous-python-and-databases/
异步的价值在于处理有数据依赖的并发请求,测试没覆盖这类场景
这话没错,但没有测试到异步真正大放异彩的常见场景。想象加载社交网络的个人主页:要取用户信息、N 张照片、每张照片的热门评论、每个评论者的头像,这些请求存在数据依赖。用阻塞 I/O 会让等待时间正比于请求数量,而异步可以让等待时间正比于依赖树的深度。所以关键不是抽象意义上异步是否更快,而是真实开发者用不用异步会如何解决同一个问题。(来源:我在 FB 多年从事这方面的基础设施工作)
ahupp查看英文原文
This is true as far as it goes, but is not testing the (very common) areas where async shines. Imagine you're loading a profile page on some social networking site. You fetch the user's basic info, and then the information for N photos, and then from each photo the top 2 comments, and for each comment the profile pic of the commentor. You can't just fetch all this in one shot because there's data dependencies. So you start fetching with blocking IO, but that makes your wait time for this request proportional to the number of fetches, which might be large. So instead, you ideally want your wait to be proportional to the depth of your dependency tree. But composing all these fetches that way is hard without the right abstraction. You can cobble it together with callbacks but it gets hairy fast. So (outside of extreme scenarios) it's not really about whether async is abstractly faster than sync. It's about how real developers would solve the same problem with/without async. (Source: I worked on product infrastructure in this area for many years at FB)
数据库与服务器同机部署掩盖了异步在远程 I/O 场景下的优势
这个基准的错误在于把数据库和服务器放在同一台机器上。异步解决的是访问昂贵外部资源时不想阻塞其他请求的问题,同机测试掩盖了这一成本。如果查询需要 1 秒,同步服务器的吞吐量会大受影响,而异步服务器依然表现良好。
srcreigh查看英文原文
The error(?) in this benchmark is putting the DB on the same machine as the server. Async solves the problem of not wanting to block other requests while you go out to expensive external resources. If you benchmark stuff while it's all on the same machine, it masks that cost. I couldn't find the size of the DB. Is it 100GB of data? or more like 10MB? Doing one point query on 10MB might not touch even disk a whole lot. If you were using srcreigh's hosted DB service and queries took 1s to run, the sync servers throughput would suffer greatly, meanwhile async servers would still perform pretty good.
基准中请求都很快,体现不出异步应对慢请求阻塞的价值
由于这个基准的性质,每个请求都要求在 10-20ms 内完成,这种情况下很难抵消异步的调度开销。但如果 10% 的请求要花 10 秒,你的 16 个 worker 很快会全部卡死,无法响应新请求——这才是异步要解决的问题。不该用消防水管浇花,也不该用洒水器灭火。
huntertwo查看英文原文
Every request is expected to be completed in 10ms-20ms because of the nature of this benchmark test. In these cases it is unlikely you're going to beat the scheduling overhead to make async worth it. However, if 10% of your requests take 10s, your 16 workers are very soon going to become 16 stuck workers and you won't be able to fulfill any new requests. This is the problem that async solves. You shouldn't use a fire hose to water your plants but you also shouldn't use a sprinkler system to put out a fire.
批评 Python 语言级 async/await 增加了生态复杂度却收益有限
总的来说,Python 的异步故事让我极其失望。他们花了十年把它融入语言核心,结果并不比 gevent、twisted 这些老牌框架更好。而现在所有人都被迫支持它,因为到处都是“带颜色的函数”,库代码必须应对事件循环内外各种使用方式。语言层面的 async/await 完全是个骗局,给我 Nginx+Lua,让我写普通的过程式代码,底层运行时替我处理挂起和恢复。
VWWHFSfQ查看英文原文
Overall I've been extremely unimpressed with Python's async story. They've spent a decade baking into the core of the language and the result is that it's no better than just using gevent or twisted or any of those other "old school" frameworks. Except now everyone is forced to support it because there are colored functions everywhere and you have to write library code that has to deal with all the myriad ways your stuff can be used depending on if you're in an event loop or not. It's baffling. Async/await at the language level is a total scam. Give me Nginx+Lua and let me just write my regular procedural code and the underlying runtime will handle yielding/resuming for me.
历次讨论
同类还值得读
开源应用架构
The Architecture of Open Source Applications
《开源软件架构》(AOSA)是一本在线免费书,邀请知名开源项目的作者亲自剖析其内部设计,涵盖 SQLAlchemy、LLVM、Audacity、Apache、Bash 等系统。
编程与软件工程上首页 9 次 · 2011–2024aosabook.org编辑精选编程语言简史:简短、不完整且多半错误
A Brief, Incomplete, and Mostly Wrong History of Programming Languages
一篇以戏谑、故意不准确的方式回顾编程语言史的博客文章,用夸张轶事和讽刺定义串起 Lisp、C、C++、Java、JavaScript、PHP 等语言。
编程与软件工程上首页 8 次 · 2009–2021james-iry.blogspot.comSQLite 是如何测试的
How SQLite is tested
SQLite 官方文档页,系统介绍 SQLite 的测试体系:多种测试方法(TCL 测试、模糊测试、回归测试、静态分析等)和海量测试用例,展示了这个小型开源项目如何靠极致测试获得…
编程与软件工程上首页 8 次 · 2009–2025sqlite.org编辑精选高级编程语言(2009)
Advanced programming languages (2009)
Matt Might 的经典文章,按用途推荐“高级编程语言”:如果追求就业就选主流语言,如果追求表达力、学术研究或创业,则推荐 Haskell、OCaml、Scala 等函数式语…
编程与软件工程上首页 8 次 · 2009–2022matt.might.net我的 Forth 与栈机器经历
Yosefk - My history with Forth & stack machines
作者以亲身经历回顾了使用 Forth 语言和栈式机器的历程:既赞叹其简洁优雅、代码量小、可自举的优点,也坦率指出栈操作在实际编程中的痛苦,以及 Forth 社区文化中的问题。
编程与软件工程上首页 8 次 · 2010–2024yosefk.com编辑精选