Golang 应用打包上线后,性能问题往往不再是“能不能跑”,而是“在当前机器和真实负载下会不会变慢、吃掉多少资源、瓶颈到底在哪里”。如果直接对着二进制文件盲目压测,通常只能看到响应变差,却很难判断问题出在函数实现、内存分配,还是服务并发处理路径。
这篇文章把 Ubuntu 环境下常见的 Go 性能测试手段拆成一条完整流程:先清理测试环境,再用基准测试拿到函数级指标,用 pprof 定位 CPU 和内存热点,最后用压测工具模拟真实请求。照着做,你不仅能得到 ns/op、B/op、allocs/op 这类结果,也能判断下一步该优化算法、内存分配还是服务配置。
先把测试环境稳定下来
性能测试最怕环境噪声。在 Ubuntu 上开着浏览器、下载工具、桌面程序做压测,CPU 和内存资源会被后台任务抢占,结果很容易失真。
更稳妥的做法是先收敛变量:
- 关闭不必要的后台程序,避免额外的 CPU、内存和磁盘占用;
- 尽量切换到纯命令行环境执行测试,减少图形界面对资源的干扰;
- 保证同一轮测试使用一致的机器配置和运行方式,便于前后对比。
这一步不直接产出性能数据,但它决定了后续结果是否有参考价值。
补齐或集成基准测试代码
如果你手里还有源码,优先使用 Go 自带的基准测试。它适合回答“某个函数或代码路径本身快不快、是否分配了太多内存”这类问题。
做法是在项目里新增一个以 _test.go 结尾的文件,例如 mypackage_test.go,然后编写以 Benchmark 开头的函数。参数类型必须是 *testing.B,循环次数由 b.N 控制,Go 会自动调整它,直到测出相对稳定的结果。
package mypackage
import "testing"
// 被测试的业务函数(示例)
func Add(a, b int) int {
return a + b
}
// 基准测试函数:测试Add函数的性能
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(1, 2) // 替换为需要测试的业务函数调用
}
}
如果项目已经只剩下 .deb、.tar.gz,或者只有最终二进制文件,那么这类基准测试代码需要重新集成回源码;如果暂时做不到,就把重点放到后面的 pprof 和压测环节,直接针对程序运行态观察表现。
运行基准测试并看懂三个核心指标
写好基准测试后,用 go test 直接执行。常用参数有三个:

-bench=.:运行当前目录下全部基准测试,也可以写成具体函数名,例如-bench=BenchmarkAdd;-cpu=4:指定测试使用的 CPU 核心数,默认是 1;-benchtime=5s:设置单个基准测试运行时长,默认 1 秒,时间拉长后结果通常更稳。
示例命令如下:
cd /path/to/source/code
# 进入源码目录
go test -bench=. -cpu=4 -benchtime=5s
跑完后,重点看这三个指标:
ns/op:每次操作平均耗时,单位纳秒;B/op:每次操作分配的内存字节数;allocs/op:每次操作的内存分配次数。
通常它们都是越低越好。简单理解:
ns/op高,说明执行路径本身偏慢;B/op和allocs/op高,说明内存分配偏多,GC 压力也可能随之增大。
这一步适合做函数级对比,例如改动某个算法前后,或者替换某个数据结构之后,结果是否明显改善。
用 pprof 深挖 CPU 和内存瓶颈
基准测试能告诉你“慢不慢”,但不一定能告诉你“为什么慢”。要定位热点函数、排查高分配路径,仍然要靠 Go 自带的 pprof。

CPU 分析:先采样,再看热点函数
一种常见做法是在程序里导入 net/http/pprof,启动一个本地 HTTP 服务,暴露调试接口:
import _ "net/http/pprof"
func main() {
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil)) // 启动pprof服务
}()
// 业务代码...
}
程序运行后,可以用 curl 抓取 30 秒 CPU profile:
curl -o cpu.prof http://localhost:6060/debug/pprof/profile?seconds=30
# 采集30秒CPU数据
然后使用:
go tool pprof cpu.prof
进入 pprof 交互界面后:
- 输入
top,查看 CPU 占用最高的函数; - 输入
web,生成可视化图,需要预先安装 graphviz。
如果你的打包流程支持,也可以在打包前通过 -cpuprofile 参数直接生成 CPU profile 文件。
内存分析:重点看分配量和分配来源
内存问题的分析方式与 CPU 类似。你可以通过 -memprofile 生成 profile 文件,也可以在代码中调用 pprof.WriteHeapProfile 输出堆信息。
分析命令如下:
go tool pprof -alloc_space mem.prof
# 查看内存分配情况
这里更值得关注的是“哪些函数在持续制造分配”,尤其是频繁创建切片、map、临时对象的路径。很多服务在吞吐量上不去时,问题并不是纯计算慢,而是分配太多、GC 太频繁。
对 Web 服务做并发压测
如果你的 Go 程序是 HTTP API 或其他 Web 服务,只做基准测试还不够。因为线上问题往往出现在并发请求、连接数增加、响应时间抖动这些场景里。

这时要用压测工具模拟真实负载,重点观察吞吐量、延迟和错误率。
使用 wrk 观察吞吐量和延迟
wrk 是 Ubuntu 上常见的高性能 HTTP 压测工具。安装与运行方式如下:
# 安装wrk(Ubuntu)
sudo apt update && sudo apt install wrk
# 运行压测:2个线程、100个并发连接、持续30秒
wrk -t2 -c100 -d30s -L http://localhost:8080/api/endpoint
示例输出:
Running 30s test @ http://localhost:8080/api/endpoint
2 threads and 100 connections
Thread Stats A vg Stdev Max +/- Stdev
Latency 50.12ms 10.23ms 120.45ms 75.32%
Req/Sec 1.23k 234.56 1.56k 68.42%
73800 requests in 30.10s, 9.87MB read
Requests/sec: 2451.80
Transfer/sec: 0.33MB
这组结果里,常见的判断重点是:
Requests/sec:整体吞吐量,越高越好;Latency:请求延迟,越低越好;Req/Sec:每线程每秒请求数,可用于观察各线程负载表现。
使用 hey 快速生成固定请求量
如果你希望更直接地控制总请求数和并发数,可以使用 hey:
# 安装hey
go install github.com/rakyll/hey@latest
# 运行压测:发送10000个请求,200个并发
hey -n 10000 -c 200 http://localhost:8080/api/endpoint
wrk 更适合持续压测观察吞吐表现,hey 则更适合快速模拟一批固定规模请求。两者都可以作为打包后验证服务性能的常用工具。
怎么根据结果决定优化方向
性能测试的价值,不在于“跑出一份报告”,而在于把结果映射到明确的优化动作。
- 如果基准测试里的
ns/op偏高,先检查算法、循环逻辑和数据结构选择是否合理; - 如果
B/op或allocs/op偏大,优先减少不必要分配,例如对象复用,或使用sync.Pool; - 如果 pprof 显示某个函数是 CPU 热点,就集中优化那条调用链,而不是泛泛调整整个项目;
- 如果 pprof 显示分配集中在切片、map 或临时对象构造,说明问题可能出在内存使用方式;
- 如果压测结果吞吐量低、延迟高,就要回头检查并发处理模型,以及数据库连接池等外部依赖配置,例如
sql.DB的MaxOpenConns。
优化完成后,继续重复基准测试、pprof 分析和压测三个环节,确认指标是否真的改善。只有在同一套条件下复测,前后结果才有比较意义。
一套适合打包后验证的 Go 性能测试路径
如果你的目标是在 Ubuntu 上把 Go 打包程序测透,一条实用路径通常是这样的:先保证环境干净,再用 go test 看函数级表现,用 pprof 找 CPU 和内存热点,最后通过 wrk 或 hey 评估真实服务负载下的吞吐与延迟。
这样做的好处是,数据会逐层收敛:先知道哪里慢,再知道为什么慢,最后验证在并发场景下是否真的改善。对已经打包部署的 Go 应用来说,这比单次跑程序或只看资源占用更接近实际问题。













