优化 Go 语言数据打包:性能基准测试与分析
优化 Go 语言数据打包:性能基准测试与分析
场景:在局域网内,需要将多个机器网卡上抓到的数据包同步到一个机器上。
原有方案:tcpdump -w 写入文件,然后定时调用 rsync 进行同步。
改造方案:使用 Go 重写这个抓包逻辑及同步逻辑,直接将抓到的包通过网络发送至服务端,由服务端写入,这样就减少了一次落盘的操作。
构造一个 pcap 文件很简单,需要写入一个 pcap文件头,后面每一条数据增加一个元数据进行描述。
使用 pcapgo 即可实现这个功能,p.buffer[:ci.CaptureLength] 为抓包的数据。
ci := gopacket.CaptureInfo{
CaptureLength: int(n),
Length: int(n),
Timestamp: time.Now(),
}
if ci.CaptureLength > len(p.buffer) {
ci.CaptureLength = len(p.buffer)
}
w.WritePacket(ci, p.buffer[:ci.CaptureLength])
为了通过区分是哪个机器过来的数据包需要增加一个 Id,算上元数据和原始数据包,表达结构如下
// from github.com/google/gopacket
type CaptureInfo struct {
// Timestamp is the time the packet was captured, if that is known.
Timestamp time.Time `json:"ts" msgpack:"ts"`
// CaptureLength is the total number of bytes read off of the wire.
CaptureLength int `json:"cap_len" msgpack:"cap_len"`
// Length is the size of the original packet. Should always be >=
// CaptureLength.
Length int `json:"len" msgpack:"len"`
// InterfaceIndex
InterfaceIndex int `json:"iface_idx" msgpack:"iface_idx"`
}
type CapturePacket struct {
CaptureInfo
Id uint32 `json:"id" msgpack:"id"`
Data []byte `json:"data" msgpack:"data"`
}
有一个细节待敲定,抓到的包使用什么结构发送至服务端?json/msgpack/自定义格式?
json/msgpack 都有对应的规范,通用性强,不容易出 BUG,性能会差一点。
自定义格式相比 json/msgpack 而言,可以去掉不必要的字段,连 key 都可以不用在序列化中出现,并且可以通过一些优化减少内存的分配,缓解gc压力。
自定义二进制协议优化思路如下
- CaptureInfo/Id 字段直接固定N个字节表示,对于 CaptureLength/Length 可以直接使用 2 个字节来表达,Id 如果数量很少使用 1 个字节来表达都可以
- 内存复用
- Encode 逻辑内部不分配内存,这样直接写入外部的 buffer,如果外部 buffer 是同步操作的话,整个逻辑 0 内存分配
- Decode 内部不分配内存,只解析元数据和复制 Data 切片,如果外部是同步操作,同样整个过程 0 内存分配
- 如果是异步操作,那么在调用 Encode/Decode 的地方对 Data 进行复制,这里可以使用 sync.Pool 进行优化,使用四个 sync.Pool 分别分配 128/1024/8192/65536 中数据
sync.Pool 的优化点有两个
- 异步操作下每个 Packet.Data 都需要有自己的空间,不能进行复用,使用 sync.Pool 来构造属于 Packet 的空间
- 元数据序列化固定字节长度的 buffer,使用 make 或者数组都会触发 gc
func acquirePacketBuf(n int) ([]byte, func()) {
var (
buf []byte
putfn func()
)
if n <= CapturePacketMetaLen+128 {
smallBuf := smallBufPool.Get().(*[CapturePacketMetaLen + 128]byte)
buf = smallBuf[:0]
putfn = func() { smallBufPool.Put(smallBuf) }
} else if n <= CapturePacketMetaLen+1024 {
midBuf := midBufPool.Get().(*[CapturePacketMetaLen + 1024]byte)
buf = midBuf[:0]
putfn = func() { midBufPool.Put(midBuf) }
} else if n <= CapturePacketMetaLen+8192 {
largeBuf := largeBufPool.Get().(*[CapturePacketMetaLen + 8192]byte)
buf = largeBuf[:0]
putfn = func() { largeBufPool.Put(largeBuf) }
} else {
xlargeBuf := xlargeBufPool.Get().(*[CapturePacketMetaLen + 65536]byte)
buf = xlargeBuf[:0]
putfn = func() { xlargeBufPool.Put(xlargeBuf) }
}
return buf, putfn
}
func (binaryPack) EncodeTo(p *CapturePacket, w io.Writer) (int, error) {
buf := metaBufPool.Get().(*[CapturePacketMetaLen]byte)
defer metaBufPool.Put(buf)
binary.BigEndian.PutUint64(buf[0:], uint64(p.Timestamp.UnixMicro()))
...
return nm + nd, err
}
数据包构造大小(By 通义千问)
方法 | 原始数据长度 (字节) | 编码后数据长度 (字节) | 变化字节数 (字节) |
---|---|---|---|
Binary Pack | 72 | 94 | +22 |
Binary Pack | 1024 | 1046 | +22 |
Binary Pack | 16384 | 16406 | +22 |
MsgPack | 72 | 150 | +78 |
MsgPack | 1024 | 1103 | +79 |
MsgPack | 16384 | 16463 | +79 |
Json Pack | 72 | 191 | +119 |
Json Pack | 1024 | 1467 | +443 |
Json Pack | 16384 | 21949 | +5565 |
Json Compress Pack | 72 | 195 | +123 |
Json Compress Pack | 1024 | 1114 | +90 |
Json Compress Pack | 16384 | 15504 | -120 |
分析
Binary Pack:
- 对于较小的数据(72字节),编码后增加了22字节。
- 对于较大的数据(16384字节),编码后增加了22字节。
- 总体来看,Binary Pack的编码效率较高,增加的字节数相对较少。
MsgPack:
- 对于较小的数据(72字节),编码后增加了78字节。
- 对于较大的数据(16384字节),编码后增加了79字节。
- MsgPack的编码效率在小数据量时不如Binary Pack,但在大数据量时仍然保持较高的效率。
Json Pack:
- 对于较小的数据(72字节),编码后增加了119字节。
- 对于较大的数据(16384字节),编码后增加了5565字节。
- Json Pack的编码效率较低,特别是对于大数据量,增加的字节数较多。
Json Compress Pack:
- 对于较小的数据(72字节),编码后增加了123字节。
- 对于较大的数据(16384字节),编码后增加了120字节。
- Json Compress Pack在小数据量时增加的字节数较多,但在大数据量时增加的字节数较少,表明压缩效果较好。
通过这个表格,你可以更直观地看到不同数据打包方法在不同数据量下的表现。希望这对你有帮助!
benchmark
json
可以看到使用 buffer 进行复用提升比较明显,主要还是减少内存分配带来的提升。
BenchmarkJsonPack/encode#72-20 17315143 647.1 ns/op 320 B/op 3 allocs/op
BenchmarkJsonPack/encode#1024-20 4616841 2835 ns/op 1666 B/op 3 allocs/op
BenchmarkJsonPack/encode#16384-20 365313 34289 ns/op 24754 B/op 3 allocs/op
BenchmarkJsonPack/encode_with_buf#72-20 24820188 447.4 ns/op 128 B/op 2 allocs/op
BenchmarkJsonPack/encode_with_buf#1024-20 13139395 910.6 ns/op 128 B/op 2 allocs/op
BenchmarkJsonPack/encode_with_buf#16384-20 1414260 8472 ns/op 128 B/op 2 allocs/op
BenchmarkJsonPack/decode#72-20 8699952 1364 ns/op 304 B/op 8 allocs/op
BenchmarkJsonPack/decode#1024-20 2103712 5605 ns/op 1384 B/op 8 allocs/op
BenchmarkJsonPack/decode#16384-20 159140 73101 ns/op 18664 B/op 8 allocs/op
msgpack
同样看到使用 buffer 进行复用的提升,和 json 的分水岭大概在 1024 字节左右,超过这个大小 msgpack 速度快很多,并且在解析的时候内存占用不会随数据进行增长。
BenchmarkMsgPack/encode#72-20 10466427 1199 ns/op 688 B/op 8 allocs/op
BenchmarkMsgPack/encode#1024-20 6599528 2132 ns/op 1585 B/op 8 allocs/op
BenchmarkMsgPack/encode#16384-20 1478127 8806 ns/op 18879 B/op 8 allocs/op
BenchmarkMsgPack/encode_with_buf#72-20 26677507 388.2 ns/op 192 B/op 4 allocs/op
BenchmarkMsgPack/encode_with_buf#1024-20 31426809 400.2 ns/op 192 B/op 4 allocs/op
BenchmarkMsgPack/encode_with_buf#16384-20 22588560 494.5 ns/op 192 B/op 4 allocs/op
BenchmarkMsgPack/decode#72-20 19894509 654.2 ns/op 280 B/op 10 allocs/op
BenchmarkMsgPack/decode#1024-20 18211321 664.0 ns/op 280 B/op 10 allocs/op
BenchmarkMsgPack/decode#16384-20 13755824 769.1 ns/op 280 B/op 10 allocs/op
压缩的效果
在内网的情况下,带宽不是问题,这个压测结果直接被 Pass
BenchmarkJsonCompressPack/encode#72-20 19934 709224 ns/op 1208429 B/op 26 allocs/op
BenchmarkJsonCompressPack/encode#1024-20 17577 766349 ns/op 1212782 B/op 26 allocs/op
BenchmarkJsonCompressPack/encode#16384-20 11757 860371 ns/op 1253975 B/op 25 allocs/op
BenchmarkJsonCompressPack/decode#72-20 490164 28972 ns/op 42048 B/op 15 allocs/op
BenchmarkJsonCompressPack/decode#1024-20 187113 71612 ns/op 47640 B/op 23 allocs/op
BenchmarkJsonCompressPack/decode#16384-20 35790 346580 ns/op 173352 B/op 30 allocs/op
自定义二进制协议
对于序列化和反序列化在复用内存后,速度的提升非常明显,在同步的操作下,能做到 0 字节分配。异步场景下,使用 sync.Pool 内存固定字节分配(两个返回值在堆上分配)
BenchmarkBinaryPack/encode#72-20 72744334 187.1 ns/op 144 B/op 2 allocs/op
BenchmarkBinaryPack/encode#1024-20 17048832 660.6 ns/op 1200 B/op 2 allocs/op
BenchmarkBinaryPack/encode#16384-20 2085050 6280 ns/op 18495 B/op 2 allocs/op
BenchmarkBinaryPack/encode_with_pool#72-20 34700313 109.2 ns/op 64 B/op 2 allocs/op
BenchmarkBinaryPack/encode_with_pool#1024-20 39370662 101.1 ns/op 64 B/op 2 allocs/op
BenchmarkBinaryPack/encode_with_pool#16384-20 18445262 177.2 ns/op 64 B/op 2 allocs/op
BenchmarkBinaryPack/encode_to#72-20 705428736 16.96 ns/op 0 B/op 0 allocs/op
BenchmarkBinaryPack/encode_to#1024-20 575312358 20.78 ns/op 0 B/op 0 allocs/op
BenchmarkBinaryPack/encode_to#16384-20 100000000 113.4 ns/op 0 B/op 0 allocs/op
BenchmarkBinaryPack/decode_meta#72-20 1000000000 2.890 ns/op 0 B/op 0 allocs/op
BenchmarkBinaryPack/decode_meta#1024-20 1000000000 2.886 ns/op 0 B/op 0 allocs/op
BenchmarkBinaryPack/decode_meta#16384-20 1000000000 2.878 ns/op 0 B/op 0 allocs/op
BenchmarkBinaryPack/decode_with_pool#72-20 106808395 31.51 ns/op 16 B/op 1 allocs/op
BenchmarkBinaryPack/decode_with_pool#1024-20 100319094 35.94 ns/op 16 B/op 1 allocs/op
BenchmarkBinaryPack/decode_with_pool#16384-20 26447718 138.6 ns/op 16 B/op 1 allocs/op
总结一下
通义千问的
Binary Pack:
- encode_to:性能最优,几乎没有内存分配,适用于高性能要求的场景。
- encode_with_pool:使用内存池优化,显著减少了时间和内存开销,适用于大多数场景。
- encode:标准方法,时间和内存开销较高。
MsgPack:
- encode_with_buf:使用预分配的缓冲区,显著减少了时间和内存开销,适用于大多数场景。
- encode:标准方法,时间和内存开销较高。
- decode:解码性能一般,内存开销较高。
Json Pack:
- encode_with_buf:使用预分配的缓冲区,显著减少了时间和内存开销,适用于大多数场景。
- encode:标准方法,时间和内存开销较高。
- decode:解码性能较差,内存开销较高。
Json Compress Pack:
- encode:标准方法,时间和内存开销非常高,不推荐用于高性能要求的场景。
- decode:解码性能较差,内存开销较高。
我总结的
在内网的环境进行传输,一般网络带宽不会成为瓶颈,所以可以不用考虑数据压缩,上面结果也看到压缩非常占用资源;
如果对数据内容不关心且数据量非常多的情况下(比如传输 pcap 包),那么使用自定义协议可能更合适一些,固定长度的元数据解析起来优化空间巨大,二进制解析比 json/msgpack 快内存分配也非常少。
引用
- 构造数据包的 Benchkark 结果,https://github.com/zxhio/benchmark/tree/main/pack
优化 Go 语言数据打包:性能基准测试与分析的更多相关文章
- KubeCon 2021|使用 eBPF 代替 iptables 优化服务网格数据面性能
作者 刘旭,腾讯云高级工程师,专注容器云原生领域,有多年大规模 Kubernetes 集群管理及微服务治理经验,现负责腾讯云服务网格 TCM 数据面产品架构设计和研发工作. 引言 目前以 Istio[ ...
- DedeCMS数据负载性能优化方案简单几招让你提速N倍
前文介绍了DedeCMS栏目列表页实现完美分页的方法,避免了大部分重复栏目标题对搜索引擎的影响,对SEO更有利.今天,分享一下DedeCMS数据负载性能优化的方法. 接触织梦也有三年多时间了,对它可谓 ...
- 大数据应用之HBase数据插入性能优化实测教程
引言: 大家在使用HBase的过程中,总是面临性能优化的问题,本文从HBase客户端参数设置的角度,研究HBase客户端数据批量插入性能优化的问题.事实胜于雄辩,数据比理论更有说服力,基于此,作者设计 ...
- mongodb可以通过profile来监控数据 (mongodb性能优化)
mongodb可以通过profile来监控数据 (mongodb性能优化) 开启 Profiling 功能 ,对慢查询进行优化: mongodb可以通过profile来监控数据,进行优化. 查看 ...
- 【转载】使用Exp和Expdp导出数据的性能对比与优化
转自:http://blog.itpub.net/117319/viewspace-1410931/ 序:这方面的文章虽然很多人写过,但是结合实际进行详细的对比分析的不多,这里,结合所在公司的行业,进 ...
- oracle数据迁移之Exp和Expdp导出数据的性能对比与优化
https://wangbinbin0326.github.io/2017/03/31/oracle%E6%95%B0%E6%8D%AE%E8%BF%81%E7%A7%BB%E4%B9%8BExp%E ...
- 【SQL server初级】数据库性能优化一:数据库自身优化(大数据量)
数据库优化包含以下三部分,数据库自身的优化,数据库表优化,程序操作优化.此文为第一部分 数据库性能优化一:数据库自身优化 优化①:增加次数据文件,设置文件自动增长(粗略数据分区) 1.1:增加次数据文 ...
- 最全 webpak4.0 打包性能优化清单
最全 webpak4.0 打包性能优化清单 webpack4.0如何进行打包优化? 无非是从两个角度进行优化,其一:优化打包速度,其二:优化打包体积,送你一份打包性能优化清单 1.使用loader的时 ...
- webpack 打包性能优化
webpack 打包性能优化 开启多线程打包 thread-loader https://www.npmjs.com/package/thread-loader https://github.com/ ...
- SQL Server 性能优化之——系统化方法提高性能
SQL Server 性能优化之——系统化方法提高性能 阅读导航 1. 概述 2. 规范逻辑数据库设计 3. 使用高效索引设计 4. 使用高效的查询设计 5. 使用技术分析低性能 6. 总结 1. 概 ...
随机推荐
- oeasy教您玩转vim - 83 - # 表达式Expression
表达式 expression 回忆 关于 函数function ,我们回忆一下 可以查询 可以新建 可以调用 还可以删除 我想用 函数function 往 buffuer 里面写点东西 比如写一个 ...
- vue进阶一~数据响应式,数据响应到视图层,手写v-model,订阅发布者模式,
1,数据响应式 当数据发生改变的时候,我们立即知道数据发生改变,并做出相关的操作:发送请求,打印文字,操作DOM等. 1.1,vue实现数据响应的原理 vue中使用了两种模式来实现数据响应式,分别是v ...
- XR实时云渲染:助力虚拟仿真实训教学呈现
近年来以5G.云计算.大数据.物联网.人工智能.虚拟现实/增强现实为代表的新兴技术迅速发展加速创新,日益融入经济社会发展各领域全过程,世界主要国家和地区纷纷加快数字化转型战略布局. 虚拟仿真系统是一种 ...
- 【Kafka】01 基于Docker环境的单例Kafka搭建
安装参考: https://www.cnblogs.com/vipsoft/p/13233045.html 环境安装需要 Zookeeper + Kafka 要学习Kafka还需要繁琐的安装配置,所以 ...
- 【Linux】Re02
一.运行启动级别 0 关机 1 单用户 2 多用户状态没有网络服务 3 多用户状态存在网络服务 4 系统未使用保留给用户 5 图形界面 6 重启 命令: init [0 - 6] 图形化界面级别需要对 ...
- 【Hibernate】Re01.5 API
1.Session单表的CRUD操作 1.增加或者修改,使用同一个方法,或者下面的两个也行: 感觉多此一举... 2.删除方法,硬删除: 3.获取方法提供了两种,Get & Load get方 ...
- 【转载】 使用t-SNE可视化图像embedding
原文地址: https://zhuanlan.zhihu.com/p/81400277 ================================================ t-SNE(t ...
- 在IIS上部署ASP.NET Core Web API和Blazor Wasm详细教程
前言 前段时间我们完成了七天.NET 8 操作 SQLite 入门到实战的开发系列教程,有不少同学留言问如何将项目发布部署到IIS上面运行.本篇文章我们就一起来讲讲在IIS上部署ASP.NET Cor ...
- FreeSWITCH对接http协议的tts服务
操作系统 :CentOS 7.6_x64 FreeSWITCH版本 :1.10.9 FreeSWITCH里面有个mod_tts_commandline模块,可以用来对接http协议的tts服务,今 ...
- Atcoder ABC364 D-F
Atcoder ABC364 D-F D - K-th Nearest 链接: D - K-th Nearest (atcoder.jp) 简要题意: 问题陈述 在一条数线上有 \(N+Q\) 个点 ...