分享
  1. 首页
  2. 文章

Python 的 PyPy 能追上 Go 的性能吗?

wangzhongyang007 · · 196 次点击 · · 开始浏览

在我们选择用哪种编程语言进行后端开发的时候,Python 和 Go 似乎代表了两种极端: Python 以**人生苦短我用Python**的开发效率闻名,却经常因性能被调侃为**慢如龟速**; Go 则以**编译即部署的轻量和高并发性能**成为云原生时代的宠儿,却因语法简陋被吐槽**开发像搬砖**。 而 PyPy 的出现,像给 Python 注射了一剂强心针,这个基于 JIT(即时编译)的 Python 解释器,宣称能让 Python 代码运行速度提升 5-10 倍。于是我们难免好奇:**有了 PyPy,Python 能在后端开发中追上甚至超越 Go 的性能吗?** 今天就用 3 个后端核心场景(高并发 API、数据处理管道、长连接服务)的实测数据,结合底层原理和实战案例,一次性说清这个问题。 ## 后端开发的性能到底指什么? 讨论性能前,得先统一标准。对后端服务而言,**性能从来不是单维度的谁跑得更快**,而是以下三个核心指标的综合: - **吞吐量(QPS)**:单位时间内能处理的请求数,直接决定服务承载能力; - **延迟(P99/P999)**:极端情况下的响应时间,影响用户体验(比如支付接口的卡顿); - **资源效率**:相同负载下的 CPU/内存占用,直接关系服务器成本。 我们选择的测试环境: - 硬件:2 核 4G 云服务器(模拟中小型后端服务的常见配置); - 语言版本:Python 3.11(CPython)、PyPy 7.3.11(对应 Python 3.9)、Go 1.21; - 框架:Python 用 FastAPI(异步),Go 用 Gin(高性能框架); - 测试工具:wrk(压测 HTTP 服务)、cProfile(Python 性能分析)、pprof(Go 性能分析)。 ## 场景一:高并发 API 服务 后端最常见的场景:实现一个用户信息查询API,接收用户 ID,从 Redis 查缓存,返回 JSON 结果。这是典型的 I/O 密集型任务。 ### 代码实现(核心逻辑) ```python # Python(FastAPI + asyncio + aioredis) from fastapi import FastAPI import aioredis import json app = FastAPI() redis = None @app.on_event("startup") async def startup(): global redis redis = await aioredis.from_url("redis://localhost") @app.get("/user/{user_id}") async def get_user(user_id: str): data = await redis.get(f"user:{user_id}") return json.loads(data) if data else {"error": "not found"} ``` ```go // Go(Gin + redis/go-redis) package main import ( "encoding/json" "github.com/gin-gonic/gin" "github.com/redis/go-redis/v9" "context" ) var rdb *redis.Client var ctx = context.Background() func main() { rdb = redis.NewClient(&redis.Options{Addr: "localhost:6379"}) r := gin.Default() r.GET("/user/:user_id", func(c *gin.Context) { userID := c.Param("user_id") data, err := rdb.Get(ctx, "user:"+userID).Bytes() if err != nil { c.JSON(404, gin.H{"error": "not found"}) return } var res map[string]interface{} json.Unmarshal(data, &res) c.JSON(200, res) }) r.Run(":8000") } ``` ### 压测结果(100 并发用户,持续 60 秒) | 指标 | Python(CPython) | Python(PyPy) | Go(Gin) | |---------------------|-------------------|----------------|-----------| | 平均 QPS | 2800 | 4200 | 12500 | | P99 延迟 | 85ms | 52ms | 12ms | | 稳定后内存占用 | 180MB | 210MB | 45MB | | CPU 峰值占用 | 85% | 72% | 68% | ### 关键结论: 1. **PyPy 确实比 CPython 快**:QPS 提升 50%,延迟降低 40%,但距离 Go 仍有 3 倍差距; 2. **GIL 是 Python 绕不过的坎**:即使 PyPy 用 JIT 加速了代码执行,但 Python 的全局解释器锁(GIL)导致多线程无法真正并行,当并发超过 50 时,PyPy 的性能增长就会停滞,而 Go 的 goroutine 能轻松利用多核,QPS 随并发数线性增长; 3. **Go 的资源效率碾压**:相同 QPS 下,Go 的内存占用仅为 PyPy 的 1/5,这意味着部署时 Go 服务能少用 4/5 的服务器。 ## 场景二:数据处理管道(PyPy 最接近 Go 的领域) 后端另一个高频场景:处理批量数据(比如解析日志、清洗用户行为数据)。这属于 CPU 密集型任务,也是 PyPy 的主场,JIT 编译对循环、计算密集的代码优化效果最明显。 我们测试"**解析 10 万条 JSON 日志(每条约 500B),提取关键字段并统计 UV(独立用户数)**"的任务。 ### 代码核心逻辑 ```python # Python 版本(用 ujson 加速解析) import ujson from collections import defaultdict def process_logs(logs): uv = defaultdict(int) for log in logs: data = ujson.loads(log) uv[data["user_id"]] += 1 return len(uv) # 读取 10 万条日志并处理(省略文件读取代码) ``` ```go // Go 版本(标准库 encoding/json) package main import ( "encoding/json" "os" "bufio" "strconv" ) type Log struct { UserID string `json:"user_id"` } func processLogs(logs []string) int { uv := make(map[string]int) for _, log := range logs { var data Log json.Unmarshal([]byte(log), &data) uv[data.UserID]++ } return len(uv) } // 读取 10 万条日志并处理(省略文件读取代码) ``` ### 执行结果(单进程处理时间) | 实现方式 | 耗时 | 对比(相对 CPython) | |-------------------|--------|----------------------| | Python(CPython) | 8.2 秒 | 100%(基准) | | Python(PyPy) | 1.5 秒 | 18%(快 5.5 倍) | | Go(原生) | 1.1 秒 | 13%(快 7.4 倍) | ### 关键结论: 1. **PyPy 在 CPU 密集场景接近 Go**:仅比 Go 慢 36%,这是两者差距最小的场景; 2. **Go 的静态类型优势显现**:Go 的 struct 解析 JSON 比 Python 的动态字典更快,且编译期类型检查避免了运行时类型错误; 3. **PyPy 的局限性**:如果代码中大量使用 C 扩展(比如用 pandas 处理数据),PyPy 会退化到 CPython 水平(因为 C 扩展绕过 JIT),而 Go 标准库的纯 Go 实现无此问题。 ## 场景三:长连接服务 后端的硬骨头场景:长连接服务(比如即时通讯、物联网设备推送),需要同时维持 1 万个以上的 TCP 连接,并实时处理消息。这类场景对并发调度和内存占用要求极高。 我们测试"**维持 1 万连接,每 10 秒向每个连接推送一条 100B 消息**"的服务表现。 ### 核心实现差异 - Python(PyPy + asyncio):用 asyncio 维护连接池,依赖事件循环处理 I/O,但每个连接仍需占用一定内存(约 4-5KB); - Go:用 goroutine 绑定每个连接(每个 goroutine 初始栈仅 2KB),配合 channel 做消息分发,调度由内核级线程管理。 ### 测试结果 | 指标 | Python(PyPy + asyncio) | Go | |---------------------|--------------------------|----| | 稳定连接数上限 | 约 8000(超过后频繁断连) | 轻松支持 5 万+ | | 1 万连接内存占用 | 450MB | 65MB | | 消息推送延迟(P99) | 320ms | 18ms | ### 关键结论: 1. **PyPy 无法突破 Python 的并发模型瓶颈**:即使 JIT 加速了单条消息处理,asyncio 的事件循环仍需在单线程内调度所有任务,高并发下会出现**惊群效应**; 2. **Go 的 goroutine 是降维打击**:轻量的协程+M:N 调度模型,让 Go 能以极低的资源占用维持海量连接,这也是 Go 成为云原生网关、消息队列(如 NSQ)首选语言的核心原因。 ## 为什么 PyPy 追不上 Go? 从测试数据看,PyPy 确实能让 Python 在部分场景接近 Go,但始终无法全面超越,核心原因在三个层面: 1. **语言设计目标不同** Python 从诞生就是**开发者友好**优先,动态类型、灵活语法带来了开发效率,但也让 JIT 优化难以做到极致(比如无法提前确定变量类型);而 Go 是**服务友好**优先,静态类型、极简语法牺牲了部分开发便捷性,却为编译优化和并发调度扫清了障碍。 2. **并发模型的本质差异** Python(包括 PyPy)的并发被 GIL 锁死:同一时间只有一个线程执行 Python 字节码,异步(asyncio)只是**单线程内的任务切换**,无法利用多核;而 Go 的 goroutine 是**真正的并行**,由 runtime 调度到不同内核线程,能充分发挥多核 CPU 性能。 3. **生态与部署的隐性成本** PyPy 虽然兼容大部分纯 Python 库,但对 C 扩展(如 numpy、psycopg2)的支持很差,而后端开发常用的数据库驱动、加密库多依赖 C 扩展;反观 Go,标准库自带电池,且编译为单二进制文件,部署时无需依赖解释器,这在容器化场景中优势巨大。 ## 什么时候用 PyPy?什么时候必须选 Go? 与其纠结谁更快,不如根据场景选工具: | 场景 | 推荐选择 | 核心理由 | |-------------------------------|----------------|--------------------------------------------------------------------------| | 中小流量 API(QPS < 5000) | Python(PyPy) | 开发快,PyPy 足以应对,且团队无需切换技术栈 | | 高并发 API(QPS > 1 万) | Go | 资源效率高,能省服务器成本,且延迟更稳定 | | 数据处理脚本(离线任务) | PyPy | 比 CPython 快 5 倍以上,且无需重写代码 | | 实时数据处理(流处理) | Go | 低延迟+高吞吐,适合做 Kafka 消费者、日志聚合服务 | | 长连接服务(IM、物联网) | Go | goroutine 对海量连接的支持碾压 Python | | 云原生工具(网关、算子) | Go | 编译后体积小(通常 < 20MB),启动快(毫秒级),适合容器化部署 | **一句话总结**:PyPy 固然能优化性能,但只能让 Python 在它的舒适区(中小流量、离线任务)跑得更快;而 Go 是后端性能的**天花板**,在高并发、资源敏感场景中,目前仍无替代者。 **最后抛出一个问题**:如果你的团队正在用 Python 开发后端,且性能遇到瓶颈,你会选择**迁移到 PyPy** 还是 **重写成 Go?** 欢迎在评论区分享你的经验〜 >我是王中阳,靠敲代码在北京买房的程序员,目前专注程序员的就业陪跑和职场晋升。有这方面需求可以联系我,聪明的你一定能想到办法的。

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
196 次点击
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏