惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

www.infosecurity-magazine.com
www.infosecurity-magazine.com
aimingoo的专栏
aimingoo的专栏
博客园 - 叶小钗
Jina AI
Jina AI
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
人人都是产品经理
人人都是产品经理
博客园_首页
Last Week in AI
Last Week in AI
酷 壳 – CoolShell
酷 壳 – CoolShell
T
Tailwind CSS Blog
云风的 BLOG
云风的 BLOG
腾讯CDC
V
Visual Studio Blog
宝玉的分享
宝玉的分享
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
P
Palo Alto Networks Blog
Know Your Adversary
Know Your Adversary
NISL@THU
NISL@THU
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
I
InfoQ
Simon Willison's Weblog
Simon Willison's Weblog
H
Help Net Security
AWS News Blog
AWS News Blog
P
Proofpoint News Feed
月光博客
月光博客
T
Threatpost
Recent Announcements
Recent Announcements
G
Google Developers Blog
Security Latest
Security Latest
T
Threat Research - Cisco Blogs
Blog — PlanetScale
Blog — PlanetScale
A
Arctic Wolf
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Latest news
Latest news
T
The Exploit Database - CXSecurity.com
Security Archives - TechRepublic
Security Archives - TechRepublic
Google DeepMind News
Google DeepMind News
Google DeepMind News
Google DeepMind News
V
V2EX
P
Proofpoint News Feed
Y
Y Combinator Blog
The Register - Security
The Register - Security
N
News | PayPal Newsroom
L
Lohrmann on Cybersecurity
H
Hacker News: Front Page
MongoDB | Blog
MongoDB | Blog
量子位
T
Troy Hunt's Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Recent Commits to openclaw:main
Recent Commits to openclaw:main

郑文峰的博客

使用dify对接飞书多维表格 使用n8n对接飞书多维表格 服务启动时出现 OOM 一次服务升级时pg表DDL执行超时失败 Go语言高效IO缓冲技术详解 Go语言延迟初始化(Lazy Initialization)最佳实践 Go语言字符串拼接性能对比与优化指南 Go语言结构体内存对齐完全指南 Go语言空结构体:零内存消耗的高效编程 Go语言堆栈分配与逃逸分析深度解析 Go语言原子操作完全指南 Go语言内存预分配完全指南 Go语言不可变数据共享:无锁并发编程实践 Go语言遍历性能深度解析:从原理到优化实践 Go语言Interface Boxing原理与性能优化指南 Go协程池深度解析:原理、实现与最佳实践 使用etcd分布式锁导致的协程泄露与死锁问题 基于pre-commit的Python代码规范落地实践 初识 MCP Server pulsar阻塞导致logstash无法接入日志 django-prometheus使用及源码分析 kube-proxy源码分析 kubernetes service如何通过iptables转发 tcp缓存引起的日志丢失 django-apschedule定时任务异常停止 理解calico容器网络通信方案原理 理解flannel的三种容器网络方案原理 理解Linux IPIP隧道 理解VXLAN网络 理解Linux TunTap设备 快速了解iptables kafka中listener和advertised.listeners的作用 django rest_framework 分页 django后端服务、logstash和flink接入VictoriaMetrics指标监控 python中import原理 docker容器单机网络 手动实现docker容器bridge网络模型 mysql之MVCC原理 mysql之日志 使用java开发logstash的filter插件 使用python实现单例模式的三种方式 redis之缓存 redis之分片集群 redis之哨兵机制 redis之主从库同步 redis之持久化 redis之五种基本数据类型 go中如何处理error pod中将代码与运行环境分离 ddt源码分析 python装饰器的使用方法 读书笔记:如何阅读一本书 使用ddt实现unittest的参数化测试 使用kubeadm安装k8s 优化gin表单的错误提示信息 gin中validator模块的源码分析 go简单使用grpc python简单使用grpc k8s之PV、PVC和StorageClass k8s之StatefulSet k8s之DaemonSet k8s之Job和CronJob k8s之ConfigMap和Secret k8s之Service k8s之Pod k8s之Deployment 容器的本质 docker容器 python迭代器与生成器 python元编程 python垃圾回收机制 python上下文管理器 django rest_framework使用jwt django rest_framework异常处理 django rest_framework 自定义文档 django压缩文件下载 django rest_framework使用pytest单元测试 django restframework choice 自定义输出数据 django Filtering 使用 django viewset 和 Router 配合使用时报的错 django model的序列化 django中使用AbStractUser django.core.exceptions.ImproperlyConfigured Application labels aren't unique, duplicates users django 中 media配置 django 外键引用自身和on_delete参数 django 警告 while time zone support is active Flask使用flask_socketio实现websocket flask结合mongo tornado 文件上传 tornado 使用jwt完成用户异步认证 tornado 用户密码 bcrypt加密 tornado 结合wtforms使用表单操作 tornado finish和write区别 tornado 使用peewee-async 完成异步orm数据库操作 pyspark streaming简介 和 消费 kafka示例 使用hue创建ozzie的pyspark action workflow count的性能优化 django rest_framework Authentication django celery 结合使用 网站
Go语言零拷贝技术完全指南
zhengwenfeng · 2025-06-14 · via 郑文峰的博客

# 前言

零拷贝(Zero-Copy)是高性能编程中的关键技术,它通过避免不必要的数据复制,可以显著提升I/O密集型应用的性能。本文将深入探讨:

  • 零拷贝的核心原理与优势
  • Go语言中实现零拷贝的三种主要方式
  • 实际场景中的性能对比数据
  • 如何根据业务场景选择最佳方案

通过本文,你将掌握如何在自己的项目中应用这些技术来提升性能。

# io.CopyBuffer

在读写时使用 io.Reader 和 io.Writer 接口时,可以使用 io.CopyBuffer 来提供重复使用的缓存区,避免了重复的分配中间的缓存。

func StreamData(src io.Reader, dst io.Writer) error {
    buf := make([]byte, 4096) // Reusable buffer
    _, err := io.CopyBuffer(dst, src, buf)
    return err
}

1
2
3
4
5

# 高效数据访问切片

切片的底层是一个数组,所以基于该数组返回一个切片可以减少内存的拷贝,而不是将切片拷贝到一个新的切片中

func process(buffer []byte) []byte {
    return buffer[128:256] // returns a slice reference without copying
}

1
2
3

Benchmark

针对显示复制和零复制切片的性能基准测试比较

func BenchmarkCopy(b *testing.B) {
	data := make([]byte, 64*1024)
	for range b.N {
		buf := make([]byte, len(data))
		copy(buf, data)
	}
}

func BenchmarkSlice(b *testing.B) {
	data := make([]byte, 64*1024)
	for range b.N {
		_ = data[:]
	}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14

运行结果如下,BenchmarkCopy 每次都会分配新的内存,并进行数据的复制操作,而 BenchmarkSlice都是对相同的数组进行切片,没有任何的分配内存和复制数据的操作,BenchmarkSlice无论是运行还是内存上都优于 BenchmarkCopy

$ go test -bench=. -benchmem .  
goos: darwin
goarch: arm64
pkg: main/demo
cpu: Apple M4 Pro
BenchmarkCopy-12          356439              3546 ns/op           65536 B/op          1 allocs/op
BenchmarkSlice-12       1000000000               0.2336 ns/op          0 B/op          0 allocs/op
PASS
ok      main/demo       2.918s

1
2
3
4
5
6
7
8
9

# mmap

正常读取文件时,是需要将磁盘中的文件读取到内核空间,再复制到用户空间进行访问。

17498741316891749874131178.png

而使用mmap的方式,是直接将磁盘中的文件映射到内存中直接访问,减少了将内核空间与用户空间数据的复制。

17498741136871749874112969.png

代码示例

import "golang.org/x/exp/mmap"

func ReadFileZeroCopy(path string) ([]byte, error) {
    r, err := mmap.Open(path)
    if err != nil {
        return nil, err
    }
    defer r.Close()

    data := make([]byte, r.Len())
    _, err = r.ReadAt(data, 0)
    return data, err
}

1
2
3
4
5
6
7
8
9
10
11
12
13

基准测试

使用 os.Open 和 mmap.Open 对 5M 文件的读取性能的基准测试。

func BenchmarkReadWithCopy(b *testing.B) {
	f, err := os.Open("./5MB.bin")
	if err != nil {
		b.Fatalf("failed to open file: %v", err)
	}
	defer f.Close()

	buf := make([]byte, 4*1024*1024) // 4MB buffer
	for range b.N {
		_, err := f.ReadAt(buf, 0)
		if err != nil && err != io.EOF {
			b.Fatal(err)
		}
	}
}

func BenchmarkReadWithMmap(b *testing.B) {
	r, err := mmap.Open("./5MB.bin")
	if err != nil {
		b.Fatalf("failed to mmap file: %v", err)
	}
	defer r.Close()

	buf := make([]byte, r.Len())
	for range b.N {
		_, err := r.ReadAt(buf, 0)
		if err != nil && err != io.EOF {
			b.Fatal(err)
		}
	}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

运行结果如下,使用mmap比标准读要快不少,内存申请上也要少。

$ go test -bench=. -benchmem .              
goos: darwin
goarch: arm64
pkg: main/demo
cpu: Apple M4 Pro
BenchmarkReadWithCopy-12           13136             90918 ns/op             319 B/op          0 allocs/op
BenchmarkReadWithMmap-12           19350             65262 ns/op             270 B/op          0 allocs/op
PASS
ok      main/demo       7.067s

1
2
3
4
5
6
7
8
9

# 总结

根据测试结果,我们得出以下结论:

  1. 缓冲区重用

    • 使用io.CopyBuffer重用缓冲区
    • 适合流式数据传输场景
  2. 切片操作

    • 优先使用切片引用而非完整复制
    • 特别适合大块数据处理
  3. 文件读取

    • 大文件处理优先考虑mmap
    • 注意mmap的内存映射特性
  4. 适用场景

    • 高吞吐量网络应用
    • 大文件处理
    • 内存敏感型应用

记住:零拷贝虽好,但也要考虑代码可读性和维护成本,在性能关键路径上使用效果最佳!