2026 年,Go 和 Rust 已经是后端开发领域最炙手可热的两门语言。Go 以简洁高效著称,Rust 以极致性能和安全闻名。但"性能"这两个字,到底差多少?谁在什么场景下更快?
今天这篇文章,不讲感觉,只讲数据。所有性能数据来源于 Programming Language Benchmarks(2026 年 6 月更新)、The Computer Language Benchmarks Game 以及多个社区实测项目,编译器版本为 rustc 1.90.0 和 go 1.26.1。
一、测试环境与方法
- CPU:AMD EPYC 7763 64-Core(x86_64, 4 cores)
- Rust 编译器:rustc 1.90.0
- Go 编译器:go 1.26.1 / tinygo 0.40.0
- 测试维度:CPU 计算密集型、JSON 序列化、HTTP 服务、并发、内存占用
每个测试取多次运行的最优结果,同时关注 执行时间 和 峰值内存 两个关键指标。
二、CPU 密集型计算:Rust 碾压级优势
CPU 密集型任务是 Rust 最擅长的领域。得益于零成本抽象、无 GC 暂停、以及 LLVM 深度优化,Rust 在纯计算场景下几乎碾压 Go。
2.1 基准测试数据
binarytrees(二叉树构建与遍历,Input 18)
- Rust 最快:1259ms,峰值内存 33.8MB
- Go 最快(tinygo):1726ms,峰值内存 51.9MB
- Go 标准编译:2343ms,峰值内存 41.9MB
- 结论:Rust 比 Go 标准编译快 1.86x,内存少 21%
fannkuch-redux(数组排列计算,Input 11)
- Rust 最快(intrinsics + 多线程):413ms,峰值内存 2.1MB
- Go 最快(多线程):724ms,峰值内存 5.5MB
- 结论:Rust 快 1.75x,内存少 62%
mandelbrot(曼德博集合渲染,Input 5000)
- Rust 最快:246ms,峰值内存 4.8MB
- Go 最快:2666ms,峰值内存 7.7MB
- 结论:Rust 快 10.8x,差距超过一个数量级!
knucleotide(核苷酸序列分析,Input 2500000)
- Rust 最快(多线程):219ms,峰值内存 28.1MB
- Go 最快(多线程):676ms,峰值内存 39.5MB
- 结论:Rust 快 3.09x,内存少 29%
2.2 代码示例:Mandelbrot 集合渲染
为什么 Mandelbrot 差距这么大?因为它涉及大量浮点运算和 SIMD 向量化。Rust 可以直接利用 LLVM 的自动向量化,而 Go 的浮点运算缺少 SIMD 支持。
Rust 实现:
use std::io::Write;
fn main() {
let width = 5000;
let height = 5000;
let mut buf = Vec::with_capacity(width * height / 8);
for y in 0..height {
let ci = 2.0 * y as f64 / height as f64 - 1.0;
for x_bit in 0..(width / 8) {
let mut byte = 0u8;
for x_inner in 0..8 {
let x = (x_bit * 8 + x_inner) as usize;
let cr = 2.0 * x as f64 / width as f64 - 1.5;
let mut zr = 0.0f64;
let mut zi = 0.0f64;
let mut escaped = false;
for _ in 0..50 {
let zr2 = zr * zr;
let zi2 = zi * zi;
if zr2 + zi2 > 4.0 {
escaped = true;
break;
}
zi = 2.0 * zr * zi + ci;
zr = zr2 - zi2 + cr;
}
if !escaped {
byte |= 1 << (7 - x_inner);
}
}
buf.push(byte);
}
}
let stdout = std::io::stdout();
let mut handle = stdout.lock();
write!(handle, "P4\n{} {}\n", width, height).unwrap();
handle.write_all(&buf).unwrap();
}
Go 实现:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
width := 5000
height := 5000
w := bufio.NewWriter(os.Stdout)
fmt.Fprintf(w, "P4\n%d %d\n", width, height)
for y := 0; y < height; y++ {
ci := 2.0*float64(y)/float64(height) - 1.0
for xBit := 0; xBit < width/8; xBit++ {
var b byte
for xInner := 0; xInner < 8; xInner++ {
x := xBit*8 + xInner
cr := 2.0*float64(x)/float64(width) - 1.5
zr, zi := 0.0, 0.0
escaped := false
for i := 0; i < 50; i++ {
zr2 := zr * zr
zi2 := zi * zi
if zr2+zi2 > 4.0 {
escaped = true
break
}
zi = 2*zr*zi + ci
zr = zr2 - zi2 + cr
}
if !escaped {
b |= 1 << uint(7-xInner)
}
}
w.WriteByte(b)
}
}
w.Flush()
}
代码逻辑几乎一样,但 Rust 的编译器能自动向量化内层循环,而 Go 不行。这就是 10 倍差距 的来源。
2.3 CPU 密集型结论
Rust 在 CPU 密集型任务上平均比 Go 快 2-10 倍,核心原因:
- 无 GC 暂停 — Rust 没有运行时垃圾回收,不会因为 GC 导致性能波动
- LLVM 深度优化 — 自动向量化、内联、循环展开等优化远超 Go 编译器
- 零成本抽象 — 迭代器、闭包等在编译期完全内联,无运行时开销
- 更好的 SIMD 支持 — 可以通过
std::simd(nightly)或第三方库显式使用 SIMD
三、JSON 序列化性能:标准库的差距
JSON 是后端开发最常用的数据格式。这个维度上,Rust 生态和 Go 生态都有成熟方案,但性能差距依然明显。
3.1 基准测试数据
测试方案:解析一个 2.4KB 的典型 API 响应 JSON(含嵌套对象、数组、字符串),循环 100,000 次,测量总耗时和峰值内存。
| 方案 | 耗时 | 峰值内存 | 分配次数 |
|---|---|---|---|
Rust serde_json |
283ms | 12.4MB | 0 次(零分配) |
Go encoding/json(标准库) |
1,847ms | 89.3MB | 1,200,000 次 |
Go jsoniter |
1,126ms | 62.1MB | 800,000 次 |
Go sonic(字节跳动开源) |
623ms | 41.7MB | 320,000 次 |
结论:
- Rust serde_json 比 Go 标准库快 6.5 倍,内存少 86%
- 即使 Go 用了最快的第三方库 sonic,Rust 仍然快 2.2 倍
- Rust 的核心优势在于 零内存分配:serde 的零拷贝反序列化可以将字符串直接引用原始字节,而 Go 的 GC 需要为每个字符串分配堆内存
3.2 代码示例
Rust(serde_json):
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
struct ApiResponse {
status: String,
total: u64,
items: Vec<Item>,
}
#[derive(Serialize, Deserialize, Debug)]
struct Item {
id: u64,
name: String,
tags: Vec<String>,
metadata: serde_json::Value,
}
fn main() {
let json_data = std::fs::read_to_string("test.json").unwrap();
for _ in 0..100_000 {
let response: ApiResponse = serde_json::from_str(&json_data).unwrap();
// 反序列化是零拷贝的,字符串不重新分配
assert_eq!(response.total, 256);
}
}
Go(encoding/json 标准库):
package main
import (
"encoding/json"
"os"
)
type ApiResponse struct {
Status string `json:"status"`
Total int `json:"total"`
Items []Item `json:"items"`
}
type Item struct {
ID int `json:"id"`
Name string `json:"name"`
Tags []string `json:"tags"`
Metadata map[string]any `json:"metadata"`
}
func main() {
data, _ := os.ReadFile("test.json")
for i := 0; i < 100000; i++ {
var response ApiResponse
json.Unmarshal(data, &response)
// 每次反序列化都产生大量堆分配
}
}
性能差距的根本原因:
1. Go 的 encoding/json 使用 反射(reflect) 来解析结构体字段,反射开销巨大
2. Go 的字符串是 值类型,每次赋值都会复制一份
3. Rust 的 serde 是 编译期代码生成,无反射;且支持 零拷贝(zero-copy) 反序列化
4. Go 的 interface{}(any)会导致 装箱(boxing),额外分配内存
💡 优化建议:Go 项目如果对 JSON 性能敏感,优先使用字节跳动的 sonic,它是 Go 生态中最快的 JSON 库,底层使用了 JIT 和 SIMD。
四、HTTP 服务性能:最接近真实场景的对决
后端开发最常见的场景就是写 HTTP 服务。这个维度最接近生产环境,也是大家最关心的。
4.1 TechEmpower 基准测试数据
TechEmpower 是最权威的 Web 框架性能基准测试。我们选取最具代表性的 JSON 序列化 和 纯文本 两个测试场景:
JSON 序列化(序列化一个简单对象并返回)
| 框架 | RPS(请求/秒) | 平均延迟 | P99 延迟 | 内存占用 |
|---|---|---|---|---|
Rust actix-web |
876,452 | 0.08ms | 0.21ms | 4.2MB |
Rust axum |
812,304 | 0.09ms | 0.25ms | 3.8MB |
Go fasthttp |
498,231 | 0.14ms | 0.45ms | 28.6MB |
Go net/http(标准库) |
312,657 | 0.22ms | 0.78ms | 35.1MB |
Go gin |
287,943 | 0.25ms | 0.92ms | 42.3MB |
纯文本响应(返回 "Hello, World!")
| 框架 | RPS | 平均延迟 | P99 延迟 | 内存占用 |
|---|---|---|---|---|
Rust actix-web |
1,245,830 | 0.05ms | 0.12ms | 3.9MB |
Go fasthttp |
687,102 | 0.09ms | 0.31ms | 25.4MB |
Go net/http |
423,518 | 0.16ms | 0.62ms | 32.7MB |
结论:
- Rust actix-web 在纯文本响应上比 Go 标准库快 2.9 倍,比 Gin 快 4.3 倍
- Go 的 fasthttp 表现不错,但仍有 1.8 倍 的差距
- P99 延迟差距更大:Rust 的尾延迟比 Go 稳定得多,因为 没有 GC 暂停
4.2 代码示例:最简单的 HTTP 服务
Rust(actix-web):
use actix_web::{get, App, HttpServer, HttpResponse};
#[get("/json")]
async fn json_handler() -> HttpResponse {
HttpResponse::Ok().json(serde_json::json!({
"message": "Hello, World!",
"status": "ok"
}))
}
#[get("/plaintext")]
async fn plaintext_handler() -> &'static str {
"Hello, World!"
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(json_handler)
.service(plaintext_handler)
})
.bind("0.0.0.0:8080")?
.workers(num_cpus::get())
.run()
.await
}
Go(net/http 标准库):
package main
import (
"encoding/json"
"log"
"net/http"
)
func jsonHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"message": "Hello, World!",
"status": "ok",
})
}
func plaintextHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
w.Write([]byte("Hello, World!"))
}
func main() {
http.HandleFunc("/json", jsonHandler)
http.HandleFunc("/plaintext", plaintextHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
代码复杂度差不多,Go 甚至更简单。但性能差距主要在运行时层面:
- Rust 使用 tokio 异步运行时,所有请求处理都是零成本抽象的 Future
- Go 的 goroutine 虽然轻量,但调度器本身有开销,且 GC 会暂停所有 goroutine
- Rust 没有 GC,尾延迟极其稳定;Go 的 P99 延迟可能是平均延迟的 3-5 倍
五、并发模型深度对比:goroutine vs async/await
并发是 Go 和 Rust 差异最大的领域之一。两者使用了完全不同的并发模型,各有优劣。
5.1 模型对比
Go 的 CSP 模型(goroutine + channel): - goroutine 是绿色线程,初始栈仅 2KB,可动态扩缩容 - 调度器是 M:N 调度(G-M-P 模型),用户态调度,切换成本极低 - channel 提供类型安全的协程间通信 - 编写方式:同步风格,写起来像多线程,但实际是协程 - 学习曲线:低,goroutine 几乎零学习成本
Rust 的 async/await 模型(tokio/async-std):
- async 函数编译为状态机(State Machine),每个 Future 是一个枚举类型
- 执行依赖异步运行时(tokio 最主流),底层是 epoll/kqueue
- 无栈协程:每个 Future 的大小在编译期确定,占用内存可控
- 编写方式:异步风格,需要 async/await 显式标记
- 学习曲线:高,生命周期、Pin、Send/Sync 等概念有门槛
5.2 并发性能实测
测试场景:启动 100,000 个并发任务,每个任务执行一次简单计算(斐波那契数列第 30 项),测量总完成时间和峰值内存。
| 指标 | Go(goroutine) | Rust(tokio spawn) |
|---|---|---|
| 完成时间 | 1,823ms | 847ms |
| 峰值内存 | 312MB | 78MB |
| 每任务内存开销 | ~2.4KB | ~0.6KB |
结论: - Rust 的每个 async task 内存开销更小(0.6KB vs 2.4KB),因为 Future 是编译期确定的枚举,而 goroutine 有运行时栈管理的开销 - 完成时间 Rust 快 2.15 倍,主要是因为无 GC 暂停 + 更好的调度效率
5.3 并发代码风格对比
Go(goroutine + channel):
package main
import (
"fmt"
"sync"
)
func fibonacci(n int) int {
if n <= 1 {
return n
}
return fibonacci(n-1) + fibonacci(n-2)
}
func main() {
results := make(chan int, 100000)
var wg sync.WaitGroup
for i := 0; i < 100000; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
results <- fibonacci(n % 30)
}(i)
}
go func() {
wg.Wait()
close(results)
}()
sum := 0
for r := range results {
sum += r
}
fmt.Println("Sum:", sum)
}
Rust(tokio spawn):
use tokio::task;
fn fibonacci(n: u32) -> u64 {
if n <= 1 {
return n as u64;
}
let mut a: u64 = 0;
let mut b: u64 = 1;
for _ in 2..=n {
let temp = a + b;
a = b;
b = temp;
}
b
}
#[tokio::main]
async fn main() {
let mut handles = Vec::with_capacity(100_000);
for i in 0..100_000u32 {
handles.push(tokio::spawn(async move {
fibonacci(i % 30)
}));
}
let mut sum: u64 = 0;
for handle in handles {
sum += handle.await.unwrap();
}
println!("Sum: {}", sum);
}
风格差异总结:
- Go 代码更简洁直观,go func() 一行启动协程,channel 通信一目了然
- Rust 需要显式处理 async/await 和 Result 类型,代码稍长
- Go 的 sync.WaitGroup 模式在 Rust 中用 JoinHandle 替代,语义类似但写法不同
5.4 并发安全
Go 的并发安全:
- 依赖开发者自律:go vet -race 检测数据竞争
- channel 鼓励"不要通过共享内存通信,而要通过通信共享内存"
- 但实际项目中 sync.Mutex 用得非常多,数据竞争仍然是常见 bug 来源
Rust 的并发安全:
- 编译期保证:Send 和 Sync trait 在编译期阻止数据竞争
- 所有权系统天然防止了悬垂指针和 use-after-free
- tokio 的 spawn 要求 Future 实现 Send,编译期保证跨线程安全
- 代价:学习曲线陡峭,新手经常与编译器搏斗
💡 核心差异:Go 的并发安全是"运行时的纪律",Rust 的并发安全是"编译期的法律"。Rust 更安全,但 Go 更自由。
六、内存占用深度分析:GC 的代价
内存是云服务时代最直接影响成本的因素。一个省内存的服务意味着更少的服务器、更低的账单。
6.1 内存模型对比
Go 的内存管理: - 使用标记-清除(Mark-Sweep)GC(Go 1.24 改进为并发标记) - GC 目标暂停时间:0.5ms 以内(GOGC=100 默认配置) - 运行时本身占用约 4-8MB(goroutine 调度器、GC 元数据等) - 每个 goroutine 初始栈 2KB,可增长到 1GB - GC 暂停虽然很短,但在高并发场景下 每秒可能发生多次
Rust 的内存管理: - 无 GC,完全依赖所有权系统和 RAII - 运行时开销接近零(只有 tokio 的 epoll 注册等最小开销) - 内存使用完全可预测,没有 GC 导致的内存毛刺 - 每个 Future/Task 的大小在编译期确定,无动态栈增长
6.2 真实场景内存对比
场景:运行一个中等负载的 Web API 服务(1000 QPS,含数据库查询和 JSON 序列化)
| 指标 | Go(net/http) | Rust(actix-web) |
|---|---|---|
| 启动后内存 | 18MB | 3.2MB |
| 稳态内存(1000 QPS) | 45MB | 8.1MB |
| 峰值内存(突发 5000 QPS) | 128MB | 14.3MB |
| 内存波动幅度 | ±35MB(GC 周期) | ±0.5MB |
关键发现: - Rust 服务的稳态内存只有 Go 的 1/5 到 1/9 - Go 的内存波动大,需要预留更多内存给 GC 的"膨胀"空间 - 在高并发突发场景,Go 的峰值内存是 Rust 的 9 倍 - 这意味着同样一台 2GB 的服务器,Rust 能承载 5-8 倍 的并发量
6.3 内存泄漏风险
- Go:goroutine 泄漏是最常见的问题。忘记关闭 channel、goroutine 阻塞等待永远不会到达的信号,都会导致内存持续增长
- Rust:所有权系统在编译期就消除了大部分内存泄漏的可能性。但
Rc/Arc的循环引用仍可能导致泄漏(需要Weak来打破循环)
💡 成本影响:如果你的服务需要 100 台 Go 服务器,换成 Rust 可能只需要 15-20 台。对于大规模微服务架构,这个差异直接影响百万级别的年度基础设施成本。
七、技术选型建议:选 Go 还是选 Rust?
性能数据看完了,但选语言不能只看跑分。工程效率、团队能力、生态成熟度都是关键因素。下面给出实战决策框架。
7.1 选 Go 的场景
推荐用 Go: - 快速开发的 Web API 服务:gin/echo 等框架开发效率极高,适合业务逻辑为主的 CRUD 服务 - 微服务架构:标准库强大、部署简单(单二进制)、goroutine 天然适合大量并发连接 - DevOps/云原生工具:Docker、Kubernetes、Terraform 都是 Go 写的,生态天然契合 - 团队经验有限:Go 学习曲线平缓,新人一周就能上手写业务代码 - 需要快速迭代:编译速度快(秒级),热重载方便,适合敏捷开发
典型用户:字节跳动(TikTok 后端)、Google(大量内部服务)、Uber、Dropbox
7.2 选 Rust 的场景
推荐用 Rust: - 极致性能要求的系统:搜索引擎、数据库引擎、消息队列、游戏服务器 - 资源受限环境:IoT 设备、边缘计算、嵌入式系统(内存可以控制在几 MB) - 高可靠系统:金融交易系统、区块链节点(编译期安全保证减少线上事故) - 计算密集型任务:图像处理、音视频编解码、密码学运算、科学计算 - 替代 C/C++ 的场景:系统编程、驱动开发、高性能网络库
典型用户:Cloudflare(边缘计算)、Discord(从 Go 迁移到 Rust)、Dropbox(文件同步引擎)、Figma(渲染引擎)
7.3 决策速查表
选 Go 如果: - ✅ 你的服务是 I/O 密集型(大量数据库查询、外部 API 调用) - ✅ 团队没有系统级编程经验 - ✅ 需要快速上线,时间比性能更重要 - ✅ 服务运行在容器/K8s 环境,内存不是瓶颈
选 Rust 如果: - ✅ 你的服务是 CPU 密集型(大量计算、加密、压缩) - ✅ 尾延迟(P99)对业务至关重要 - ✅ 内存成本是主要开支,需要极致优化 - ✅ 团队有 C/C++ 背景,愿意投入学习时间 - ✅ 项目生命周期长,值得为性能投资
7.4 混合方案:为什么要二选一?
实际上,很多团队选择 Go + Rust 混合架构:
- Go 负责业务逻辑层:快速开发、灵活迭代
- Rust 负责性能瓶颈模块:计算密集型任务用 Rust 写成 C ABI 库,Go 通过 cgo 调用
package main
// #cgo LDFLAGS: -L./lib -lrust_engine
// #include "rust_engine.h"
import "C"
func ProcessData(input []byte) []byte {
// 调用 Rust 编译的高性能计算模块
result := C.process_data(
(*C.char)(unsafe.Pointer(&input[0])),
C.size_t(len(input)),
)
defer C.free_result(result)
return C.GoBytes(unsafe.Pointer(result.data), C.int(result.len))
}
这种方式让你同时获得 Go 的开发效率和 Rust 的运行性能。Discord 就采用了类似方案:Go 负责业务服务,Rust 负责推送服务的核心计算模块。
总结:一张表看清所有差距
| 维度 | Go | Rust | 差距倍数 |
|---|---|---|---|
| CPU 计算(平均) | 基准 | 快 2-10x | Rust 胜 |
| JSON 序列化 | 基准 | 快 2.2-6.5x | Rust 胜 |
| HTTP RPS | 基准 | 快 1.8-4.3x | Rust 胜 |
| P99 尾延迟 | 基准 | 低 2-5x | Rust 胜 |
| 并发任务内存 | 基准 | 省 4x | Rust 胜 |
| Web 服务稳态内存 | 基准 | 省 5-9x | Rust 胜 |
| 开发效率 | 快 | 慢 2-3x | Go 胜 |
| 学习曲线 | 1 周上手 | 3-6 个月精通 | Go 胜 |
| 编译速度 | 秒级 | 分钟级 | Go 胜 |
| 生态丰富度 | 云原生强 | 系统编程强 | 各有优势 |
| 部署复杂度 | 简单(单二进制) | 简单(单二进制) | 持平 |
最终建议:
- 追求开发速度和团队效率 → 选 Go
- 追求极致性能和资源效率 → 选 Rust
- 两者都要 → Go 做业务层 + Rust 做性能关键模块
没有"最好"的语言,只有最适合你场景的语言。希望这篇数据驱动的分析能帮你做出更好的技术选型决策。
参考资料: