32名工人是最佳选择 | AI生成和翻译
为什么 32 是 M2 Air 的最佳并发数
工作负载解析
每次 git fetch 的执行流程如下:
线程空闲(99%的时间)
├── DNS 查找 → GitHub
├── TCP 握手 → GitHub
├── SSH 密钥交换 / TLS 握手
├── Git 协议:"你有什么引用?"
├── Git 协议:"这是精简包"
├── 将包文件写入 .git/objects/pack/
└── 线程再次空闲
CPU 几乎无事可做——仅在最后进行 SHA-1 哈希验证。线程 99% 的时间都在等待两件事:
-
与 GitHub 的网络往返——从广州(通过 DMIT/hysteria2 途经洛杉矶代理)每次请求约 50-200ms。这是 50-200ms 的纯等待时间,线程处于休眠状态,CPU 核心完全空闲。
-
磁盘写入——你的 SSD 写入包文件仅需微秒级别。可忽略不计。
为什么 CPU 核心数在此不关键
你的 M2 有 8 个核心(4 大核 + 4 小核)。如果是 CPU 密集型任务(如视频编码),8 个线程就能占满所有核心,32 个线程只会增加上下文切换的开销。
但对于 I/O 密集型工作,等待网络数据包的线程消耗 0% CPU。它被挂起在内核的 epoll/kqueue 中——无需调度,不消耗时钟周期。因此你可以让 32 或 64 个线程同时处于”活跃”状态,而总 CPU 占用率可能只有 2-3%。
你机器上的数学计算
160 个仓库 × 约 150ms 平均网络 RTT(广州 → 洛杉矶代理 → GitHub)
使用 8 个工作者:
160 / 8 = 20 批 × 150ms = 理论最小值 3.0s
实际:24.1s(包含 SSH 建立、DNS、部分慢仓库)
使用 32 个工作者:
160 / 32 = 5 批 × 150ms = 理论最小值 0.75s
实际:9.9s(4 批遭遇慢尾延迟)
使用 64 个工作者:
160 / 64 = 2.5 批 → 3 批 × 150ms = 理论值 0.45s
实际:9.6s——仅比 32 快 0.3s
为什么 64 提升不大
三大瓶颈:
-
GitHub 连接限流——GitHub 限制每个 IP 的并发 SSH 连接数。超过约 30-40 个后,新连接会开始在服务器端排队。你在 32 个时就遇到了这个瓶颈。
-
DMIT 代理瓶颈——你的流量通过 hysteria2 → 洛杉矶 → GitHub。代理有自己的连接池和带宽限制。32 个并发流已经使其饱和。
-
macOS 文件描述符开销——每个 git 进程打开约 5-10 个文件描述符(套接字、包文件、索引等)。32 个工作者时约 200-300 个 FD,远在你的
ulimit -n 2560限制内。64 个时仍然没问题,但内核的 kqueue 需要做更多轮询工作。
为什么不选 128 或 256
工作者数 批次数 GitHub 排队 净收益
─────── ─────── ────────── ──────
8 20 无 基准值
32 5 轻微 快 2.4 倍
64 3 中等 快 2.5 倍
128 2 严重 ~2.5 倍(无提升)
256 1 严重 可能更慢
当达到 128+ 时,你建立连接的速度超过 GitHub 的接受速度,连接在内核的 TCP 积压队列中排队。你并没有加快获取速度——只是将队列从”你的线程池”转移到了”GitHub 的限流器”。同样的瓶颈,不同的队列。
结论
对于 CPU 密集型任务,最佳工作者数 = CPU 核心数(你的 M2 为 8)。对于像 git fetch 这样的 I/O 密集型任务,最佳工作者数 = 远程服务器和你的网络路径能同时处理的并发网络请求数。对于从广州通过洛杉矶代理访问 GitHub 的情况,这个数字约为 32。