ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Sliver 中基于 klauspost/compress/zstd 的 Zstandard 压缩实战:流式压缩、零分配解码与字典训练

Sliver 中基于 klauspost/compress/zstd 的 Zstandard 压缩实战:流式压缩、零分配解码与字典训练 网络安全【免费下载链接】sliverAdversary Emulation Framework项目地址https://gitcode.com/gh_mirrors/sl/sliver点击查看免费下载本篇文章以开源仓库 SliverAdversary Emulation Framework中 vendor 化的第三方依赖github.com/klauspost/compress/zstd官方 README 为核心主体系统讲解该纯 Go Zstandard 压缩库的安装引入、压缩器与解压器的完整用法、并发模型、字典机制、ZIP 内嵌集成方式以及实测性能数据并结合 Sliver 源码中 crack 文件存储对 zstd 的真实调用说明它在实际项目中的落地形态。读完本文你将掌握如何用该库编写流式/内存块级的高吞吐压缩与解压代码并根据数据特征在四种压缩等级与并发参数之间做出正确的性能取舍。Zstandard 与 klauspost/compress/zstd 概览Zstandard是该算法的 Go 实现包内实现了对 Zstandard 内容的压缩与解压能力当前实现侧重于速度表现。该包具有以下关键特性纯 Go 实现无 cgo 依赖可通过noasm与nounsafe构建标签禁用汇编加速与 unsafe 优化路径64 位优化目前针对 64 位处理器做了深度优化在 32 位处理器上速度会明显下降流式与内存块双模式既支持io.WriteCloser/io.Reader的大数据流式处理也支持EncodeAll/DecodeAll的内存块级独立任务多级压缩策略提供 Fastest / Default / Better / Best 四种预定义等级开源许可以 Go 标准许可证BSD提供。安装与引入使用 Go 模块方式安装go get -u github.com/klauspost/compress包位于github.com/klauspost/compress/zstd引入方式为import github.com/klauspost/compress/zstdSliver 仓库正是以 vendor 目录方式将其固定为项目依赖例如 server/rpc/rpc-crackstations.go 与 server/rpc/crack-queue_test.go 均直接 import 该包详见下文在 Sliver 中的实际应用一节。压缩器 Compressor成熟度与稳定性压缩器状态为STABLE稳定可能存在细微的边界 bug但已在大量不同类型的数据上经过测试并被多个项目积极使用库的每次更新都会经过持续的模糊测试fuzz testing重点保证解码器在任意输入下都不会崩溃或越界。即便如此由于仍可能存在特定数据类型 / 大小 / 参数组合触发的边界情况官方建议在正式使用前对自己的数据做针对性测试。压缩等级四种预定义 Speed当前实现了高速Fastest与中高速Default压缩器并在此基础上扩展了 Better 与 Best。四种等级与参考实现 zstd level 的对应关系如下等级常量大致等价于参考 zstd 等级定位SpeedFastestlevel 1追求最快速度压缩率最低SpeedDefaultlevel 3默认默认档位速度与压缩率均衡SpeedBetterCompressionlevel 7更高压缩率约 2~3 倍默认档 CPU 开销SpeedBestCompressionlevel 11最高压缩率不计 CPU 成本这一枚举定义与默认档位在源码 encoder_options.go 中可以验证默认选项windowSize: 8 20、level: SpeedDefault工厂函数newFastEncoder根据等级分别创建doubleFastEncoderDefault、betterFastEncoderBetter、bestFastEncoderBest等不同的编码器实现。压缩等级只能通过WithEncoderLevel()选项在创建时指定且不支持ResetWithOptions动态修改。在速度维度README 给出的对照关系是该库最快模式的压缩速度约为标准库 deflate/gzip 最快模式的2 倍默认档位的压缩率大致相当于标准库 level 3但速度通常为其3 倍。流式压缩NewWriter创建默认配置的流式压缩器并完成一次完整的输入流 → 压缩输出流拷贝// Compress input to output. func Compress(in io.Reader, out io.Writer) error { enc, err : zstd.NewWriter(out) if err ! nil { return err } _, err io.Copy(enc, in) if err ! nil { enc.Close() return err } return enc.Close() }写入enc的数据会被压缩输出Close()调用时完成最终收尾输出。即使压缩过程中出错也应调用Close()释放可能占用的资源。上述写法适合单次大体积压缩但在可能的情况下应尽量复用 writer。复用方式使用Reset(io.Writer)切换到新的输出使编码器复用全部内部资源、避免重复分配使用WithEncoderConcurrency(n)控制并发默认值为GOMAXPROCS源码见 encoder_options.go设为1可完全关闭异步 goroutine让每个块压缩完成即阻塞写入。需要注意当前流式编码自带轻量并发最多 2 个 goroutine 并行处理流的一部分这与WithEncoderConcurrency相互独立且未来可能改变。因此若希望长期限制并发应在创建时显式指定期望的并发值而不是依赖默认行为。内存块压缩EncodeAll对于小数据块编码器提供EncodeAll(src, dst []byte) []byte将src全部编码后追加到dst并返回结果import github.com/klauspost/compress/zstd // Create a writer that caches compressors. // For this operation type we supply a nil Reader. var encoder, _ zstd.NewWriter(nil) // Compress a buffer. // If you have a destination buffer, the allocation in the call can also be eliminated. func Compress(src []byte) []byte { return encoder.EncodeAll(src, make([]byte, 0, len(src))) }EncodeAll的关键语义可并发调用每次调用只在调用者自身的 goroutine 上运行多个编码块可以直接拼接拼接结果等价于对应输入流的合并压缩结果EncodeAll压缩的数据既可用流式解压也可用DecodeAll解压小型编码强烈建议复用编码器预热后即可实现近乎零分配若再提供容量充足的目标缓冲make([]byte, 0, len(src))甚至可以做到完全零分配同一编码器同时用于流式与独立块压缩是安全的可用WithEncoderConcurrency(n)限制并发编码的最大数量。其他实用选项源码 encoder_options.go 中还提供以下可选的编码器选项均以WithEncoderXxx形式传入NewWriter选项作用备注WithEncoderCRC(true)在输出中加入 4 字节 CRC 校验值输出增大 4 字节可用ResetWithOptions修改WithEncoderPadding(n)输出长度按 n 字节填充对齐填充区由crypto/rand随机数据填充EncodeAll场景下总大小取 n 的倍数WithEncoderLevel(l)指定四种预定义压缩等级创建后不可修改WithEncoderDict(dict)指定压缩字典见下文字典一节未来兼容性保证这是一个持续演进的项目使用时需注意压缩效率与速度都可能随版本变化但目标是将默认档效率维持在 zstd 默认level 3水平不得假设编码输出保持不变不要用压缩结果的哈希做相似性比对同一代码版本的编码器可认为输出确定未来可能存在破坏该保证的模式但不会在未显式开启选项的情况下启用该编码器不设计为且大概率永远不会输出与参考编码器完全一致的比特流。解压器 Decompressor解压器同样标记为STABLE并持续进行模糊测试核心目标是保证任何输入都无法让解码器崩溃或越过其运行边界。解压器支持两大使用场景大数据流与较小的内存缓冲。流式解压NewReaderimport github.com/klauspost/compress/zstd func Decompress(in io.Reader, out io.Writer) error { d, err : zstd.NewReader(in) if err ! nil { return err } defer d.Close() // Copy content... _, err io.Copy(out, d) return err }使用要点默认设置下NewReader会启动后台 goroutine不再使用时必须调用Close()以停止它们goroutine 会在返回错误包括流结束的io.EOF后自动退出流默认以4 个异步阶段并发解码以获得最大吞吐若希望完全同步、按需解压使用WithDecoderConcurrency(1)复用流式解码器使用Reset(r io.Reader) error切换输入流即使上一个流解压失败解码器也可安全复用Close()之后解码器不可再复用但所有运行中的 goroutine 都会停止因此确定不再使用时必须调用。内存块解压DecodeAllimport github.com/klauspost/compress/zstd // Create a reader that caches decompressors. // For this operation type we supply a nil Reader. var decoder, _ zstd.NewReader(nil, zstd.WithDecoderConcurrency(0)) // Decompress a buffer. We dont supply a destination buffer, // so it will be allocated by the decoder. func Decompress(src []byte) ([]byte, error) { return decoder.DecodeAll(src, nil) }DecodeAll(src, dst []byte) ([]byte, error)将解压后的数据追加到dst。要点默认创建4 个解压器支持多个缓冲区的并发解压用WithDecoderConcurrency(n)自行调整并发上限WithDecoderConcurrency(0)会创建GOMAXPROCS个解压器若提供len(dst)0且容量符合预期的目标切片可避免多余分配。零分配操作解码器在设计上支持预热后零分配运行因此应长期持有并复用解码器以获取最佳性能流式复用Reset(r io.Reader) error失败流后也可复用资源释放Close()释放后不可复用goroutine 停止缓冲解压提供length 0 期望容量的目标切片即可做到无多余分配。解压并发模型缓冲解码器在调用者 goroutine 上顺序执行、不做并发但可同时解码多个缓冲用WithDecoderConcurrency(n)限制并行数量。流式解码器则创建四类分工的 goroutine读取输入并按块切分字面量literals解压序列sequences解压重建输出流。因此流式解码器会预读并持续准备数据供输出。并发级别决定流模式下提前解码的块数。由于块之间存在前后依赖流式解码的并发收益有限——实践中通常只能有效利用约 3 个核心。字典Dictionaries支持解压使用字典压缩的数据。字典由zstd --train命令行工具基于样本数据训练生成其中包含解码器的初始状态。解压侧使用WithDecoderDicts(dicts ...[]byte)一次性注册一个或多个字典数据中指定了字典 ID 时会自动选用对应字典复用的解码器会保留已注册的字典注册多个相同 ID 的字典时最后一个生效。压缩侧使用WithEncoderDict(dict []byte)启用字典仅使用一个且即使无益于压缩也可能被使用压缩所用的字典必须用于解压对应内容字典应基于相似数据训练才有实际收益若使用不合适的字典输出可能比不用字典时略大目前使用字典压缩存在固定的启动性能开销落地前务必针对自身数据测试性能。Zstd 在 ZIP 文件中的应用该库支持用 zstandard 压缩 zip 归档中的单个文件。虽然此能力未被广泛支持但适合内部文件格式的场景。使用方法注册压缩器与解压器。强烈建议在单个 zipReader/Writer实例上注册而不要使用全局注册函数——因为来自不同包的两份全局注册会触发 panic。配套实现位于 zip.goZipCompressor(opts ...EOption)返回可注册到 zip 库的压缩器内部通过sync.Pool复用编码器ZipDecompressor(opts ...DOption)返回解压器其强制使用WithDecoderConcurrency(1)默认限制最大解压窗口为 128MB窗口大小可覆盖。最佳实践只维护单一压缩器与解压器实例因为它们可被多个 zip 文件并发使用且单实例可复用内部资源。性能基准数据以下数据摘自官方 README 的实测基准基准环境与数据集均为当时公开数据结果会随硬件与版本变化仅供参考。表中zskp指本包zstd klauspostzstd指 Datadog 的 cgo 库gzstd为 Go 标准库 gzipgzkp为本库实现的 gzipzskp的等级 1~4 分别对应 Fastest / Default / Better / Best。mb/s按 2^20 字节计算。压缩性能Silesia 语料库silesia.tar输入 211,947,520 字节库等级输出大小耗时 (ms)MB/szskp173,821,326634318.47zskp267,655,4041,508133.96zskp364,746,9333,00067.37zskp460,073,50816,92611.94cgo zstd173,605,392543371.56cgo zstd366,793,289864233.68cgo zstd662,916,4501,913105.66cgo zstd960,212,3935,06339.92gzstd180,007,7351,498134.87gzkp180,088,2721,009200.31GOB 二进制流高度可压缩输入 1,911,399,616 字节库等级输出大小耗时 (ms)MB/szskp1233,948,0963,230564.34zskp2203,997,6944,997364.73zskp3173,526,52313,435135.68zskp4162,195,23547,55938.33cgo zstd1249,810,4242,637691.26cgo zstd3208,192,1463,490522.31cgo zstd6193,632,0386,687272.56cgo zstd9177,620,38616,175112.70gzstd1357,382,0139,046201.49gzkp1359,136,6694,885373.08enwik92006-03-03 英文维基百科转储前 10^9 字节输入 1,000,000,000 字节库等级输出大小耗时 (ms)MB/szskp1343,833,6053,687258.64zskp2317,001,2377,672124.29zskp3291,915,82315,92359.89zskp4261,710,29177,69712.27cgo zstd1358,072,0213,110306.65cgo zstd3313,734,6724,784199.35cgo zstd6295,138,87510,29092.68cgo zstd9278,348,70028,54933.40gzstd1382,578,1368,608110.78gzkp1382,781,1605,628169.45高度可压缩 JSON输入 6,273,951,764 字节库等级输出大小耗时 (ms)MB/szskp1697,439,5329,789611.17zskp2610,876,53818,553322.49zskp3517,662,85844,186135.41zskp4464,617,114165,37336.18cgo zstd1766,284,0378,450708.00cgo zstd3661,889,47610,927547.57cgo zstd6642,756,85922,996260.18cgo zstd9601,974,52352,413114.16gzstd11,164,397,76826,793223.32gzkp11,120,631,85617,693338.16VM 镜像Linux Mint 安装若干应用后的 rawstudio-mint14.tar输入 8,558,382,592 字节库等级输出大小耗时 (ms)MB/szskp13,718,400,22118,206448.29zskp23,326,118,33737,074220.15zskp33,163,842,36187,30693.49zskp42,970,480,650783,86210.41cgo zstd13,609,250,10417,136476.27cgo zstd33,341,679,99729,262278.92cgo zstd63,235,846,40677,904104.77cgo zstd93,160,778,861140,94657.91gzstd13,926,234,99251,345158.96gzkp13,960,117,29836,722222.26CSV 数据nyc-taxi-data-10M.csv输入 3,325,605,752 字节库等级输出大小耗时 (ms)MB/szskp1641,319,3329,462335.17zskp2588,976,12617,570180.50zskp3529,329,26032,43297.79zskp4474,949,772138,02522.98cgo zstd1687,399,6378,233385.18cgo zstd3598,514,41110,065315.07cgo zstd6570,522,95320,038158.27cgo zstd9517,554,79764,56549.12gzstd1928,654,90821,270149.11gzkp1922,273,21413,929227.68小结在多数数据集上zskp的 Fastest等级 1与 Default等级 2在保持接近或优于 cgo zstd 同等级压缩率的同时压缩速度显著高于 Go 标准库 gzip而在任意档位下均远快于gzstd。Best 档等级 4能取得接近 cgo zstd level 9 的压缩率但耗时明显增加。解压性能以下为流式解码与小块并发解码基准AMD Ryzen 9 3950X 16 核使用 AMD64 汇编反映 2022 年 5 月前后性能可能已过时BenchmarkDecoderSilesia-32 5 206878840 ns/op 1024.50 MB/s 49808 B/op 43 allocs/op BenchmarkDecoderEnwik9-32 1 1271809000 ns/op 786.28 MB/s 72048 B/op 52 allocs/op并发块解码DecodeAllParallelpct 为不可压缩占比数据ns/opMB/s不可压缩占比B/opallocs/opkppkn.gtb.zst17,85710,321.9622.48%1020geo.protodata.zst4,42126,823.2111.89%190plrabn12.txt.zst56,8428,477.1739.90%7540lcet10.txt.zst43,9329,714.0133.27%5240asyoulik.txt.zst15,0478,319.1540.34%660alice29.txt.zst18,4368,249.6337.75%880html_x_4.zst11,52335,546.093.637%1430paper-100k.pdf.zst1,07095,720.9880.53%30fireworks.jpeg.zst1,75270,272.35100.0%50urls.10K.zst52,93413,263.3726.25%1,0140html.zst5,23219,572.2714.49%200comp-data.bin.zst1,2763,194.7131.26%00可以观察到预热后的DecodeAll在并发模式下吞吐可达数 GB/s 至数十 GB/s且绝大多数场景为零分配。在 Sliver 中的实际应用Crack 文件存储Sliver 作为对抗仿真框架其 crack密码破解子系统在文件分块上传与完整性校验流程中真实使用了该 zstd 包是理解压缩器 解压器如何配合的绝佳实例。在 server/rpc/rpc-crackstations.go 的verifyCrackFileUpload函数中服务端读取按顺序拼接的破解文件分块sequentialCrackChunkReader若crackFile.IsCompressed为真则通过zstd.NewReader(reader)将分块流包裹为解压流再以io.Copy写入 SHA-256 摘要器并核对解压后大小与期望的UncompressedSize、CompressedSize是否一致——这正是本文流式解压 Close()释放 goroutine用法的工程化体现。对应的测试 server/rpc/crack-queue_test.go 则展示了压缩侧测试构造一个 4KB 词表负载用zstd.NewWriter(compressed)写入、encoder.Close()完成收尾随后把压缩结果切成三段作为三个分块写入 chunk 目录再走完整上传校验流程断言 SHA-256 与大小均匹配。该用例同时验证了EncodeAll之外的流式写入 多分块拼接后仍可被单个zstd.NewReader正确解压的语义。贡献与许可该库欢迎社区贡献新功能/修复请附带测试性能增强请附带基准benchmark。README 同时致谢了其包含的第三方包github.com/cespare/xxhash版权归 Caleb Spare2016。对本仓库读者而言若需进一步研读实现细节可重点关注 encoder.go编码器主逻辑、decoder.go解码器主逻辑、encoder_options.go全部WithEncoderXxx选项与等级定义、decoder_options.go全部WithDecoderXxx选项以及 zip.goZIP 集成。赞分享网络安全【免费下载链接】sliverAdversary Emulation Framework项目地址https://gitcode.com/gh_mirrors/sl/sliver点击查看免费下载相关推荐Zstandard 纯 Go 压缩库完全指南基于 klauspost/compress 的高性能 zstd 压缩与解压实战Zstandard 纯 Go 压缩库完全指南基于 klauspost/compress 的高性能 zstd 压缩与解压实战 本文以仓库内 vendored 的构建工具云原生后端Cilium 中的 Zstandard 压缩klauspost/compress/zstd 纯 Go 编解码实战指南Cilium 中的 Zstandard 压缩klauspost/compress/zstd 纯 Go 编解码实战指南 本篇技术指南围绕当前 Cilium 仓库云原生网络服务网格可观测性网络安全eBPFKubeEdge 依赖树中的 Go 原生 Zstandard 压缩库klauspost/compress/zstd 压缩与解压实战指南KubeEdge 依赖树中的 Go 原生 Zstandard 压缩库klauspost/compress/zstd 压缩与解压实战指南 本文围绕 KubeEd云原生边缘计算物联网容器编排边缘网关上一篇NVIDIA Ingest日志分析实战快速定位生产环境问题下一篇TUnit测试数据生成自定义DataSourceAttribute实现创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表